diff --git a/.claude/development-guidelines.md b/.claude/development-guidelines.md new file mode 100644 index 0000000..3c21512 --- /dev/null +++ b/.claude/development-guidelines.md @@ -0,0 +1,391 @@ +# DebrosFramework Development Guidelines + +## Code Style and Standards + +### TypeScript Configuration +- **Strict Mode**: Always use strict TypeScript configuration +- **Decorators**: Enable `experimentalDecorators` and `emitDecoratorMetadata` +- **Target**: ES2020 for modern JavaScript features +- **Module System**: ES modules with CommonJS interop + +### Code Formatting +- **Prettier**: Use Prettier for consistent code formatting +- **ESLint**: Follow ESLint rules for code quality +- **Indentation**: 2 spaces for all files +- **Line Length**: Max 120 characters per line +- **Semicolons**: Always use semicolons +- **Quotes**: Single quotes for strings, double quotes for JSX + +### Naming Conventions +- **Classes**: PascalCase (e.g., `UserModel`, `QueryBuilder`) +- **Functions/Methods**: camelCase (e.g., `createUser`, `findById`) +- **Variables**: camelCase (e.g., `userId`, `queryResult`) +- **Constants**: UPPER_SNAKE_CASE (e.g., `DEFAULT_CACHE_SIZE`) +- **Files**: PascalCase for classes, camelCase for utilities +- **Interfaces**: PascalCase with descriptive names (e.g., `ModelConfig`) + +## Framework Architecture Patterns + +### Model System +```typescript +// Always extend BaseModel for data models +@Model({ + scope: 'user' | 'global', + type: 'docstore', + sharding: { strategy: 'hash', count: 4, key: 'fieldName' } +}) +export class ModelName extends BaseModel { + @Field({ type: 'string', required: true }) + propertyName: string; + + @BelongsTo(() => RelatedModel, 'foreignKey') + relation: RelatedModel; +} +``` + +### Query Patterns +```typescript +// Use chainable query builder pattern +const results = await Model.query() + .where('field', 'operator', value) + .with(['relationship1', 'relationship2']) + .orderBy('field', 'direction') + .limit(count) + .find(); + +// Prefer specific methods over generic ones +const user = await User.findById(id); // Good +const user = await User.query().where('id', id).findOne(); // Less preferred +``` + +### Error Handling +```typescript +// Always use try-catch for async operations +try { + const result = await someAsyncOperation(); + return result; +} catch (error) { + console.error('Operation failed:', error); + throw new DebrosFrameworkError('Descriptive error message', { originalError: error }); +} + +// Validate inputs early +if (!userId || typeof userId !== 'string') { + throw new ValidationError('User ID must be a non-empty string'); +} +``` + +### Service Layer Pattern +```typescript +// Encapsulate business logic in service classes +export class UserService { + async createUser(userData: CreateUserData): Promise { + // Validation + await this.validateUserData(userData); + + // Business logic + const user = await User.create(userData); + + // Post-processing + await this.sendWelcomeEmail(user); + + return user; + } + + private async validateUserData(data: CreateUserData): Promise { + // Validation logic + } +} +``` + +## Testing Standards + +### Unit Tests +- **Jest**: Use Jest for all unit testing +- **Mocking**: Mock external dependencies (OrbitDB, IPFS) +- **Coverage**: Aim for >90% code coverage +- **Structure**: One test file per source file +- **Naming**: `*.test.ts` for test files + +```typescript +describe('ModelName', () => { + let model: ModelName; + + beforeEach(() => { + model = new ModelName(); + }); + + describe('methodName', () => { + it('should handle normal case', async () => { + // Arrange + const input = 'test value'; + + // Act + const result = await model.methodName(input); + + // Assert + expect(result).toBeDefined(); + expect(result.property).toBe('expected value'); + }); + + it('should throw error for invalid input', async () => { + // Arrange & Act & Assert + await expect(model.methodName(null)).rejects.toThrow('Expected error message'); + }); + }); +}); +``` + +### Integration Tests +- **Docker**: Use Docker for real integration tests +- **Scenarios**: Test complete user workflows +- **Data**: Use realistic test data +- **Cleanup**: Always clean up test data + +## Performance Guidelines + +### Query Optimization +```typescript +// Use selective field loading when possible +const users = await User.query().select(['id', 'username']).find(); + +// Prefer eager loading for predictable relationships +const posts = await Post.query().with(['author', 'comments']).find(); + +// Use caching for expensive queries +const popularPosts = await Post.query() + .where('likeCount', '>', 100) + .cache(300) // Cache for 5 minutes + .find(); +``` + +### Memory Management +- **Lazy Loading**: Use lazy loading for large datasets +- **Pagination**: Always use pagination for large result sets +- **Cache Limits**: Set appropriate cache size limits +- **Cleanup**: Clean up resources in finally blocks + +### Database Design +- **Sharding**: Design effective sharding strategies +- **Indexing**: Use indexes for frequently queried fields +- **Relationships**: Avoid deep relationship chains +- **Data Types**: Use appropriate data types for storage efficiency + +## Security Considerations + +### Input Validation +```typescript +// Always validate and sanitize inputs +@Field({ + type: 'string', + required: true, + validate: (value: string) => value.length >= 3 && value.length <= 50, + transform: (value: string) => value.trim().toLowerCase() +}) +username: string; +``` + +### Access Control +- **User Scoping**: Use user-scoped models for private data +- **Validation**: Validate user permissions before operations +- **Sanitization**: Sanitize all user inputs +- **Encryption**: Use encryption for sensitive data + +### Error Messages +- **Security**: Don't expose internal system details in error messages +- **Logging**: Log security-relevant events +- **Validation**: Provide clear validation error messages + +## Documentation Standards + +### Code Documentation +```typescript +/** + * Creates a new user with the provided data. + * + * @param userData - The user data to create + * @param options - Additional creation options + * @returns Promise resolving to the created user + * @throws ValidationError if user data is invalid + * @throws DuplicateError if username or email already exists + * + * @example + * ```typescript + * const user = await userService.createUser({ + * username: 'alice', + * email: 'alice@example.com' + * }); + * ``` + */ +async createUser(userData: CreateUserData, options?: CreateOptions): Promise { + // Implementation +} +``` + +### README Updates +- Keep README.md up to date with latest features +- Include practical examples +- Document breaking changes +- Provide migration guides + +### API Documentation +- Document all public APIs +- Include parameter types and return types +- Provide usage examples +- Document error conditions + +## Common Patterns and Anti-Patterns + +### โœ… Good Patterns + +#### Model Definition +```typescript +@Model({ + scope: 'user', + type: 'docstore', + sharding: { strategy: 'hash', count: 4, key: 'userId' } +}) +export class Post extends BaseModel { + @Field({ type: 'string', required: true, maxLength: 200 }) + title: string; + + @Field({ type: 'string', required: true }) + content: string; + + @BeforeCreate() + setDefaults() { + this.createdAt = Date.now(); + this.updatedAt = Date.now(); + } +} +``` + +#### Service Methods +```typescript +async createPost(authorId: string, postData: CreatePostData): Promise { + // Validate input + if (!authorId) throw new ValidationError('Author ID is required'); + + // Check permissions + const author = await User.findById(authorId); + if (!author) throw new NotFoundError('Author not found'); + + // Create post + const post = await Post.create({ + ...postData, + authorId, + }); + + // Post-processing + await this.notifyFollowers(author, post); + + return post; +} +``` + +### โŒ Anti-Patterns + +#### Avoid Direct Database Access +```typescript +// Bad: Direct database manipulation +const db = await orbitdb.open('posts'); +await db.put('key', data); + +// Good: Use model methods +const post = await Post.create(data); +``` + +#### Avoid Synchronous Operations +```typescript +// Bad: Synchronous file operations +const data = fs.readFileSync('file.json'); + +// Good: Async operations +const data = await fs.promises.readFile('file.json'); +``` + +#### Avoid Deep Relationship Chains +```typescript +// Bad: Deep relationship loading +const posts = await Post.query() + .with(['author.profile.settings.preferences']) + .find(); + +// Good: Load only what you need +const posts = await Post.query() + .with(['author']) + .find(); +``` + +## Migration Guidelines + +### Schema Changes +```typescript +// Create migration for schema changes +const migration = createMigration('add_user_avatar', '1.1.0') + .addField('User', 'avatarUrl', { + type: 'string', + required: false + }) + .transformData('User', (user) => ({ + ...user, + avatarUrl: user.avatarUrl || null + })) + .build(); +``` + +### Backwards Compatibility +- Always maintain backwards compatibility in minor versions +- Deprecate features before removing them +- Provide migration paths for breaking changes +- Document all changes in CHANGELOG.md + +## Deployment Considerations + +### Environment Configuration +- Use environment variables for configuration +- Provide default configurations for development +- Validate configuration on startup +- Document required environment variables + +### Production Readiness +- Enable production optimizations +- Configure appropriate cache sizes +- Set up monitoring and logging +- Implement health checks + +### Performance Monitoring +- Monitor query performance +- Track cache hit rates +- Monitor memory usage +- Set up alerts for errors + +## Contributing Guidelines + +### Pull Request Process +1. Fork the repository +2. Create feature branch from main +3. Implement changes with tests +4. Update documentation +5. Submit pull request with description + +### Code Review Checklist +- [ ] Code follows style guidelines +- [ ] Tests are included and passing +- [ ] Documentation is updated +- [ ] No breaking changes (or properly documented) +- [ ] Performance implications considered +- [ ] Security implications reviewed + +### Commit Message Format +``` +type(scope): description + +body (optional) + +footer (optional) +``` + +Types: feat, fix, docs, style, refactor, test, chore + +These guidelines ensure consistent, maintainable, and high-quality code throughout the DebrosFramework project. diff --git a/.claude/project-overview.md b/.claude/project-overview.md new file mode 100644 index 0000000..b6fb281 --- /dev/null +++ b/.claude/project-overview.md @@ -0,0 +1,249 @@ +# DebrosFramework Project Overview + +## Project Identity +**DebrosFramework** is a powerful Node.js framework that provides an ORM-like abstraction over OrbitDB and IPFS, making it easy to build scalable decentralized applications. + +- **Package Name**: `@debros/network` +- **Version**: 0.5.1-beta (Active Development) +- **License**: GNU GPL v3.0 +- **Language**: TypeScript +- **Framework Type**: Decentralized Application Framework + +## What This Project Does + +DebrosFramework simplifies the development of decentralized applications by providing: + +### Core Capabilities +- **Model-based Abstraction**: Define data models using decorators and TypeScript classes +- **Automatic Database Management**: Handle user-scoped and global databases automatically +- **Smart Sharding**: Distribute data across multiple databases for scalability +- **Advanced Query System**: Rich query capabilities with relationship loading and caching +- **Automatic Features**: Built-in pinning strategies and PubSub event publishing +- **Migration System**: Schema evolution and data transformation capabilities +- **Type Safety**: Full TypeScript support with strong typing throughout + +### Key Features +1. **๐Ÿ—๏ธ Model-Driven Development**: Familiar decorator patterns for data models +2. **๐Ÿ” Powerful Query System**: Complex queries with relationship loading +3. **๐Ÿš€ Automatic Scaling**: Handle millions of users with automatic sharding +4. **๐Ÿ”„ Schema Evolution**: Safe data structure migrations +5. **๐Ÿ”— Rich Relationships**: Complex relationships between models +6. **โšก Performance Features**: Query caching, eager loading, optimized pagination +7. **๐ŸŽฏ Model Hooks**: Lifecycle hooks for business logic + +## Architecture Overview + +The framework is built around several core components: + +1. **Models & Decorators**: Define data structure and behavior +2. **Database Manager**: Handles database creation and management +3. **Shard Manager**: Distributes data across multiple databases +4. **Query System**: Processes queries with optimization and caching +5. **Relationship Manager**: Handles complex relationships between models +6. **Migration System**: Manages schema evolution over time +7. **Automatic Features**: Pinning, PubSub, and performance optimization + +## Technology Stack + +### Core Dependencies +- **OrbitDB**: Distributed peer-to-peer database on IPFS +- **IPFS/Helia**: Distributed file system for data storage +- **libp2p**: Peer-to-peer networking stack +- **TypeScript**: Strong typing and modern JavaScript features + +### Key Libraries +- `@orbitdb/core`: Core OrbitDB functionality +- `@helia/unixfs`: IPFS file system operations +- `@libp2p/*`: Various libp2p modules for networking +- `winston`: Logging +- `node-cache`: In-memory caching +- `express`: HTTP server for integration tests + +## Project Structure + +``` +src/framework/ +โ”œโ”€โ”€ core/ # Core framework components +โ”‚ โ”œโ”€โ”€ ConfigManager.ts +โ”‚ โ”œโ”€โ”€ DatabaseManager.ts +โ”‚ โ””โ”€โ”€ ModelRegistry.ts +โ”œโ”€โ”€ models/ # Model system and decorators +โ”‚ โ”œโ”€โ”€ BaseModel.ts +โ”‚ โ””โ”€โ”€ decorators/ +โ”œโ”€โ”€ query/ # Query builder and execution +โ”‚ โ”œโ”€โ”€ QueryBuilder.ts +โ”‚ โ”œโ”€โ”€ QueryExecutor.ts +โ”‚ โ”œโ”€โ”€ QueryOptimizer.ts +โ”‚ โ””โ”€โ”€ QueryCache.ts +โ”œโ”€โ”€ relationships/ # Relationship management +โ”‚ โ”œโ”€โ”€ RelationshipManager.ts +โ”‚ โ”œโ”€โ”€ LazyLoader.ts +โ”‚ โ””โ”€โ”€ RelationshipCache.ts +โ”œโ”€โ”€ sharding/ # Data sharding logic +โ”‚ โ””โ”€โ”€ ShardManager.ts +โ”œโ”€โ”€ migrations/ # Schema migration system +โ”‚ โ”œโ”€โ”€ MigrationManager.ts +โ”‚ โ””โ”€โ”€ MigrationBuilder.ts +โ”œโ”€โ”€ pinning/ # Automatic pinning features +โ”‚ โ””โ”€โ”€ PinningManager.ts +โ”œโ”€โ”€ pubsub/ # Event publishing system +โ”‚ โ””โ”€โ”€ PubSubManager.ts +โ”œโ”€โ”€ services/ # External service integrations +โ”‚ โ”œโ”€โ”€ OrbitDBService.ts +โ”‚ โ”œโ”€โ”€ IPFSService.ts +โ”‚ โ””โ”€โ”€ RealOrbitDBService.ts +โ””โ”€โ”€ types/ # TypeScript type definitions + โ”œโ”€โ”€ framework.ts + โ”œโ”€โ”€ models.ts + โ””โ”€โ”€ queries.ts +``` + +## Development Status + +**Current State**: Beta (v0.5.1-beta) - Active Development + +### What's Stable +- โœ… Core model system with decorators +- โœ… Basic CRUD operations +- โœ… Query builder and execution +- โœ… Relationship management +- โœ… Database management and sharding +- โœ… Migration system foundation + +### What's In Development +- ๐Ÿšง Advanced query optimization +- ๐Ÿšง Performance optimization features +- ๐Ÿšง Production-ready configurations +- ๐Ÿšง Extended relationship types +- ๐Ÿšง Enhanced error handling + +### Testing +- **Unit Tests**: Comprehensive test suite using Jest +- **Integration Tests**: Docker-based real-world scenarios +- **Blog Scenario**: Complete blogging platform test case + +## Key Concepts + +### Model Scoping +- **Global Models**: Shared across all users (e.g., User profiles) +- **User Models**: Each user has their own database instance (e.g., Posts, Comments) + +### Sharding Strategies +- **Hash Sharding**: Distribute data based on key hashing +- **Range Sharding**: Distribute data based on value ranges +- **User Sharding**: Dedicated shards per user or user group + +### Query System +- **Chainable API**: Fluent interface for building queries +- **Type Safety**: Full TypeScript support with auto-completion +- **Relationship Loading**: Eager and lazy loading strategies +- **Caching**: Intelligent query result caching + +### Relationships +- **BelongsTo**: Many-to-one relationships +- **HasMany**: One-to-many relationships +- **HasOne**: One-to-one relationships +- **ManyToMany**: Many-to-many relationships (with through tables) + +## Common Use Cases + +### 1. Social Platforms +- User profiles and authentication +- Posts, comments, and reactions +- Friend networks and messaging +- Activity feeds and notifications + +### 2. Content Management +- Blogs and publishing platforms +- Document management systems +- Media galleries and collections +- Version control for content + +### 3. Collaborative Applications +- Real-time document editing +- Project management tools +- Team collaboration platforms +- Knowledge bases and wikis + +### 4. Marketplace Applications +- Product catalogs and inventory +- User reviews and ratings +- Order management systems +- Payment and transaction records + +## Development Workflow + +### Prerequisites +- Node.js 18.0 or higher +- TypeScript knowledge +- Basic understanding of IPFS and OrbitDB concepts +- Docker (for integration tests) + +### Build Process +```bash +npm run build # Compile TypeScript +npm run dev # Development with watch mode +npm run lint # ESLint code checking +npm run format # Prettier code formatting +``` + +### Testing +```bash +npm run test:unit # Fast unit tests with mocks +npm run test:real # Full integration tests with Docker +npm run test:blog-integration # Blog scenario integration tests +``` + +### Key Development Principles +1. **Type Safety First**: Everything is strongly typed +2. **Decorator-Based**: Use decorators for configuration +3. **Async/Await**: All operations are promise-based +4. **Error Handling**: Comprehensive error management +5. **Performance**: Built-in optimization and caching +6. **Scalability**: Designed for distributed systems + +## API Patterns + +### Model Definition +```typescript +@Model({ + scope: 'user', + type: 'docstore', + sharding: { strategy: 'hash', count: 4, key: 'userId' } +}) +class Post extends BaseModel { + @Field({ type: 'string', required: true }) + title: string; + + @BelongsTo(() => User, 'userId') + user: User; +} +``` + +### Query Operations +```typescript +const posts = await Post.query() + .where('isPublished', true) + .where('createdAt', '>', Date.now() - 7 * 24 * 60 * 60 * 1000) + .with(['user', 'comments']) + .orderBy('likeCount', 'desc') + .limit(20) + .find(); +``` + +### Lifecycle Hooks +```typescript +class User extends BaseModel { + @BeforeCreate() + setupNewUser() { + this.registeredAt = Date.now(); + } + + @AfterCreate() + async sendWelcomeEmail() { + // Business logic after creation + } +} +``` + +This framework makes building decentralized applications feel like traditional web development while providing the benefits of distributed, peer-to-peer systems. diff --git a/.claude/technical-reference.md b/.claude/technical-reference.md new file mode 100644 index 0000000..2d4f84e --- /dev/null +++ b/.claude/technical-reference.md @@ -0,0 +1,607 @@ +# DebrosFramework Technical Reference + +## Core Architecture Components + +### DebrosFramework Main Class +**Location**: `src/framework/DebrosFramework.ts` + +The main framework class that orchestrates all components: + +```typescript +export class DebrosFramework { + // Core services + private orbitDBService: FrameworkOrbitDBService; + private ipfsService: FrameworkIPFSService; + + // Framework components + private databaseManager: DatabaseManager; + private shardManager: ShardManager; + private queryCache: QueryCache; + private relationshipManager: RelationshipManager; + private pinningManager: PinningManager; + private pubsubManager: PubSubManager; + private migrationManager: MigrationManager; + + // Lifecycle methods + async initialize(orbitDBService?, ipfsService?, config?): Promise + async start(): Promise + async stop(): Promise + getStatus(): FrameworkStatus + getMetrics(): FrameworkMetrics +} +``` + +### BaseModel Class +**Location**: `src/framework/models/BaseModel.ts` + +Abstract base class for all data models: + +```typescript +export abstract class BaseModel { + // Instance properties + public id: string; + public _loadedRelations: Map; + protected _isDirty: boolean; + protected _isNew: boolean; + + // Static configuration + static modelName: string; + static storeType: StoreType; + static scope: 'user' | 'global'; + static sharding?: ShardingConfig; + static fields: Map; + static relationships: Map; + + // CRUD operations + async save(): Promise + static async create(data: any): Promise + static async findById(id: string): Promise + static query(): QueryBuilder + async delete(): Promise + + // Lifecycle hooks + async beforeCreate(): Promise + async afterCreate(): Promise + async beforeUpdate(): Promise + async afterUpdate(): Promise + async beforeDelete(): Promise + async afterDelete(): Promise +} +``` + +## Decorator System + +### @Model Decorator +**Location**: `src/framework/models/decorators/Model.ts` + +Configures model behavior and database storage: + +```typescript +interface ModelConfig { + scope: 'user' | 'global'; // Database scope + type: StoreType; // OrbitDB store type + sharding?: ShardingConfig; // Data distribution strategy + pinning?: PinningConfig; // Automatic pinning configuration + pubsub?: PubSubConfig; // Event publishing configuration + validation?: ValidationConfig; // Model-level validation +} + +@Model({ + scope: 'user', + type: 'docstore', + sharding: { strategy: 'hash', count: 4, key: 'userId' } +}) +``` + +### @Field Decorator +**Location**: `src/framework/models/decorators/Field.ts` + +Defines field properties and validation: + +```typescript +interface FieldConfig { + type: FieldType; // Data type + required?: boolean; // Required field + unique?: boolean; // Unique constraint + default?: any | (() => any); // Default value + validate?: (value: any) => boolean; // Custom validation + transform?: (value: any) => any; // Data transformation + serialize?: boolean; // Include in serialization + index?: boolean; // Create index for field + virtual?: boolean; // Virtual field (not stored) +} + +@Field({ + type: 'string', + required: true, + unique: true, + validate: (value: string) => value.length >= 3, + transform: (value: string) => value.trim().toLowerCase() +}) +``` + +### Relationship Decorators +**Location**: `src/framework/models/decorators/relationships.ts` + +```typescript +// Many-to-one relationship +@BelongsTo(() => User, 'userId') +author: User; + +// One-to-many relationship +@HasMany(() => Post, 'authorId') +posts: Post[]; + +// One-to-one relationship +@HasOne(() => Profile, 'userId') +profile: Profile; + +// Many-to-many relationship +@ManyToMany(() => Tag, 'post_tags', 'tag_id', 'post_id') +tags: Tag[]; +``` + +### Lifecycle Hook Decorators +**Location**: `src/framework/models/decorators/hooks.ts` + +```typescript +@BeforeCreate() +setupDefaults() { + this.createdAt = Date.now(); +} + +@AfterCreate() +async sendNotification() { + await this.notifyUsers(); +} + +@BeforeUpdate() +updateTimestamp() { + this.updatedAt = Date.now(); +} + +@AfterUpdate() +async invalidateCache() { + await this.clearRelatedCache(); +} + +@BeforeDelete() +async checkPermissions() { + if (!this.canDelete()) { + throw new Error('Cannot delete this record'); + } +} + +@AfterDelete() +async cleanupRelations() { + await this.removeRelatedData(); +} +``` + +## Query System + +### QueryBuilder Class +**Location**: `src/framework/query/QueryBuilder.ts` + +Fluent interface for building queries: + +```typescript +export class QueryBuilder { + // Filter methods + where(field: string, value: any): QueryBuilder + where(field: string, operator: string, value: any): QueryBuilder + where(callback: (query: QueryBuilder) => void): QueryBuilder + orWhere(field: string, value: any): QueryBuilder + whereIn(field: string, values: any[]): QueryBuilder + whereNotIn(field: string, values: any[]): QueryBuilder + whereNull(field: string): QueryBuilder + whereNotNull(field: string): QueryBuilder + whereLike(field: string, pattern: string): QueryBuilder + + // Relationship methods + with(relations: string[]): QueryBuilder + withCount(relations: string[]): QueryBuilder + + // Ordering and limiting + orderBy(field: string, direction?: 'asc' | 'desc'): QueryBuilder + limit(count: number): QueryBuilder + offset(count: number): QueryBuilder + + // Field selection + select(fields: string[]): QueryBuilder + distinct(field?: string): QueryBuilder + + // Caching + cache(ttl?: number): QueryBuilder + + // Execution methods + find(): Promise + findOne(): Promise + first(): Promise + count(): Promise + exists(): Promise + paginate(page: number, perPage: number): Promise> + + // Aggregation + sum(field: string): Promise + avg(field: string): Promise + min(field: string): Promise + max(field: string): Promise +} +``` + +### Query Operators +```typescript +type QueryOperator = + | 'eq' // Equal to + | 'ne' // Not equal to + | 'gt' // Greater than + | 'gte' // Greater than or equal + | 'lt' // Less than + | 'lte' // Less than or equal + | 'in' // In array + | 'not in' // Not in array + | 'like' // Pattern matching + | 'regex' // Regular expression + | 'is null' // Is null + | 'is not null' // Is not null + | 'includes' // Array includes value + | 'includes any'// Array includes any of values + | 'includes all'// Array includes all values +``` + +## Database Management + +### DatabaseManager Class +**Location**: `src/framework/core/DatabaseManager.ts` + +Handles database creation and lifecycle: + +```typescript +export class DatabaseManager { + // Database operations + async getGlobalDatabase(modelName: string): Promise + async getUserDatabase(userId: string, modelName: string): Promise + async createDatabase(name: string, type: StoreType, options?: any): Promise + async closeDatabase(name: string): Promise + + // Document operations + async getDocument(database: Database, storeType: StoreType, id: string): Promise + async putDocument(database: Database, storeType: StoreType, id: string, data: any): Promise + async deleteDocument(database: Database, storeType: StoreType, id: string): Promise + async queryDocuments(database: Database, storeType: StoreType, query: any): Promise + + // Lifecycle + async initialize(orbitDBService: FrameworkOrbitDBService): Promise + async stop(): Promise +} +``` + +### ShardManager Class +**Location**: `src/framework/sharding/ShardManager.ts` + +Handles data distribution across shards: + +```typescript +export class ShardManager { + // Shard operations + getShardForKey(modelName: string, key: string): Shard + getShardForRange(modelName: string, value: any): Shard + getAllShards(modelName: string): Shard[] + + // Shard management + async createShards(modelName: string, config: ShardingConfig): Promise + async redistributeData(modelName: string, newShardCount: number): Promise + + // Configuration + setShardingConfig(modelName: string, config: ShardingConfig): void + getShardingConfig(modelName: string): ShardingConfig | undefined +} + +interface ShardingConfig { + strategy: 'hash' | 'range' | 'user'; + count: number; + key: string; + ranges?: ShardRange[]; +} + +interface Shard { + id: string; + database: Database; + range?: { min: any; max: any }; +} +``` + +## Relationship Management + +### RelationshipManager Class +**Location**: `src/framework/relationships/RelationshipManager.ts` + +Handles loading and caching of model relationships: + +```typescript +export class RelationshipManager { + // Relationship loading + async loadRelationship(model: BaseModel, relationshipName: string): Promise + async loadRelationships(model: BaseModel, relationshipNames: string[]): Promise + async eagerLoadRelationships(models: BaseModel[], relationshipNames: string[]): Promise + + // Relationship operations + async attachRelationship(model: BaseModel, relationshipName: string, relatedModel: BaseModel): Promise + async detachRelationship(model: BaseModel, relationshipName: string, relatedModel: BaseModel): Promise + async syncRelationship(model: BaseModel, relationshipName: string, relatedModels: BaseModel[]): Promise + + // Caching + getCachedRelationship(model: BaseModel, relationshipName: string): any + setCachedRelationship(model: BaseModel, relationshipName: string, data: any): void + clearRelationshipCache(model: BaseModel, relationshipName?: string): void +} +``` + +## Migration System + +### MigrationManager Class +**Location**: `src/framework/migrations/MigrationManager.ts` + +Handles schema evolution and data transformation: + +```typescript +export class MigrationManager { + // Migration operations + async runMigration(migrationId: string): Promise + async rollbackMigration(migrationId: string): Promise + async runPendingMigrations(): Promise + + // Migration management + registerMigration(migration: Migration): void + getPendingMigrations(): Migration[] + getAppliedMigrations(): Promise + + // Status + getMigrationStatus(): Promise +} + +interface Migration { + id: string; + version: string; + name: string; + description: string; + targetModels: string[]; + up: MigrationOperation[]; + down: MigrationOperation[]; + dependencies?: string[]; + validators?: MigrationValidator[]; +} +``` + +### MigrationBuilder Class +**Location**: `src/framework/migrations/MigrationBuilder.ts` + +Fluent interface for creating migrations: + +```typescript +export function createMigration(name: string, version: string): MigrationBuilder { + return new MigrationBuilder(name, version); +} + +export class MigrationBuilder { + // Field operations + addField(modelName: string, fieldName: string, config: FieldConfig): MigrationBuilder + removeField(modelName: string, fieldName: string): MigrationBuilder + modifyField(modelName: string, fieldName: string, config: FieldConfig): MigrationBuilder + renameField(modelName: string, oldName: string, newName: string): MigrationBuilder + + // Index operations + addIndex(modelName: string, fields: string[], options?: IndexOptions): MigrationBuilder + removeIndex(modelName: string, indexName: string): MigrationBuilder + + // Data transformation + transformData(modelName: string, transformer: (data: any) => any): MigrationBuilder + + // Validation + addValidator(name: string, validator: MigrationValidator): MigrationBuilder + + // Build migration + build(): Migration +} +``` + +## Caching System + +### QueryCache Class +**Location**: `src/framework/query/QueryCache.ts` + +Intelligent caching of query results: + +```typescript +export class QueryCache { + // Cache operations + get(key: string): Promise + set(key: string, value: any, ttl?: number): Promise + delete(key: string): Promise + clear(): Promise + + // Cache management + invalidateModelCache(modelName: string): Promise + invalidateUserCache(userId: string): Promise + + // Statistics + getStats(): CacheStats + getHitRate(): number +} +``` + +### RelationshipCache Class +**Location**: `src/framework/relationships/RelationshipCache.ts` + +Specialized caching for relationship data: + +```typescript +export class RelationshipCache { + // Relationship caching + getCachedRelationship(modelId: string, relationshipName: string): any + setCachedRelationship(modelId: string, relationshipName: string, data: any, ttl?: number): void + invalidateRelationship(modelId: string, relationshipName: string): void + + // Batch operations + preloadRelationships(modelIds: string[], relationshipNames: string[]): Promise + warmCache(modelName: string, relationshipName: string): Promise +} +``` + +## Type Definitions + +### Framework Types +**Location**: `src/framework/types/framework.ts` + +```typescript +export interface FrameworkConfig { + cache?: CacheConfig; + queryOptimization?: QueryOptimizationConfig; + automaticPinning?: PinningConfig; + pubsub?: PubSubConfig; + development?: DevelopmentConfig; +} + +export interface CacheConfig { + enabled?: boolean; + maxSize?: number; + ttl?: number; +} + +export type StoreType = 'docstore' | 'eventlog' | 'keyvalue' | 'counter' | 'feed'; +``` + +### Model Types +**Location**: `src/framework/types/models.ts` + +```typescript +export interface FieldConfig { + type: FieldType; + required?: boolean; + unique?: boolean; + default?: any | (() => any); + validate?: (value: any) => boolean; + transform?: (value: any) => any; + serialize?: boolean; + index?: boolean; + virtual?: boolean; +} + +export type FieldType = 'string' | 'number' | 'boolean' | 'array' | 'object' | 'date'; + +export interface RelationshipConfig { + type: 'belongsTo' | 'hasMany' | 'hasOne' | 'manyToMany'; + target: () => typeof BaseModel; + foreignKey: string; + localKey?: string; + through?: string; + throughForeignKey?: string; + throughLocalKey?: string; +} +``` + +### Query Types +**Location**: `src/framework/types/queries.ts` + +```typescript +export interface QueryOptions { + where?: WhereClause[]; + orderBy?: OrderByClause[]; + limit?: number; + offset?: number; + with?: string[]; + cache?: boolean | number; +} + +export interface WhereClause { + field: string; + operator: QueryOperator; + value: any; + boolean: 'and' | 'or'; +} + +export interface OrderByClause { + field: string; + direction: 'asc' | 'desc'; +} + +export interface PaginationResult { + data: T[]; + total: number; + page: number; + perPage: number; + totalPages: number; + hasNext: boolean; + hasPrev: boolean; +} +``` + +## Error Handling + +### Framework Errors +```typescript +export class DebrosFrameworkError extends Error { + code: string; + details?: any; + + constructor(message: string, code?: string, details?: any) { + super(message); + this.name = 'DebrosFrameworkError'; + this.code = code || 'UNKNOWN_ERROR'; + this.details = details; + } +} + +export class ValidationError extends DebrosFrameworkError { + field: string; + value: any; + constraint: string; +} + +export class QueryError extends DebrosFrameworkError { + query: string; + parameters?: any[]; +} + +export class RelationshipError extends DebrosFrameworkError { + modelName: string; + relationshipName: string; + relatedModel: string; +} +``` + +## Performance Optimization + +### Query Optimization +**Location**: `src/framework/query/QueryOptimizer.ts` + +```typescript +export class QueryOptimizer { + // Query optimization + optimizeQuery(query: QueryBuilder): QueryBuilder + analyzeQueryPerformance(query: QueryBuilder): Promise + suggestIndexes(modelName: string): Promise + + // Statistics + getSlowQueries(): Promise + getQueryStats(): Promise +} +``` + +### LazyLoader Class +**Location**: `src/framework/relationships/LazyLoader.ts` + +```typescript +export class LazyLoader { + // Lazy loading + async loadOnDemand(model: BaseModel, relationshipName: string): Promise + async batchLoad(models: BaseModel[], relationshipName: string): Promise + + // Configuration + setBatchSize(size: number): void + setLoadingStrategy(strategy: 'immediate' | 'batched' | 'deferred'): void +} +``` + +This technical reference provides the implementation details needed to work effectively with the DebrosFramework codebase. diff --git a/.lintstagedrc b/.lintstagedrc index 970fbfb..520a2a7 100644 --- a/.lintstagedrc +++ b/.lintstagedrc @@ -2,9 +2,15 @@ "src/**/*.{js,ts}": [ "prettier --write", "eslint --fix", + "pnpm run test:unit", "npm run build" ], + "tests/**/*.{js,ts}": [ + "prettier --write", + "eslint --fix", + "pnpm run test:unit" + ], "*.{json,md}": [ "prettier --write" ] -} \ No newline at end of file +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..28fcc99 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,347 @@ +# Contributing to DebrosFramework + +Thank you for your interest in contributing to DebrosFramework! This document provides guidelines and information for contributors. + +## Development Status + +DebrosFramework is currently in **beta (v0.5.0-beta)** and under active development. We welcome contributions from the community to help improve the framework. + +## Getting Started + +### Prerequisites + +- Node.js 18.0 or higher +- npm or pnpm package manager +- Git +- TypeScript knowledge +- Familiarity with IPFS and OrbitDB concepts + +### Development Setup + +1. **Fork and Clone** + + ```bash + git clone https://github.com/YOUR_USERNAME/network.git + cd network + ``` + +2. **Install Dependencies** + + ```bash + pnpm install + ``` + +3. **Build the Project** + + ```bash + pnpm run build + ``` + +4. **Run Tests** + + ```bash + # Unit tests + pnpm run test:unit + + # Integration tests + pnpm run test:real + ``` + +## Project Structure + +``` +src/framework/ +โ”œโ”€โ”€ core/ # Core framework components +โ”œโ”€โ”€ models/ # Model system and decorators +โ”œโ”€โ”€ query/ # Query builder and execution +โ”œโ”€โ”€ relationships/ # Relationship management +โ”œโ”€โ”€ sharding/ # Data sharding logic +โ”œโ”€โ”€ migrations/ # Schema migration system +โ”œโ”€โ”€ pinning/ # Automatic pinning features +โ”œโ”€โ”€ pubsub/ # Event publishing system +โ””โ”€โ”€ types/ # TypeScript type definitions + +docs/docs/ # Documentation source +tests/ # Test suites +โ””โ”€โ”€ real-integration/ # Integration test scenarios +``` + +## How to Contribute + +### Types of Contributions + +We welcome the following types of contributions: + +1. **๐Ÿ› Bug Reports** - Report issues and bugs +2. **โœจ Feature Requests** - Suggest new features +3. **๐Ÿ“– Documentation** - Improve docs and examples +4. **๐Ÿ”ง Code Contributions** - Bug fixes and new features +5. **๐Ÿงช Testing** - Add tests and improve test coverage +6. **๐Ÿ’ก Examples** - Create usage examples and tutorials + +### Bug Reports + +When reporting bugs, please include: + +- **Clear description** of the issue +- **Steps to reproduce** the problem +- **Expected vs actual behavior** +- **Environment details** (Node.js version, OS, etc.) +- **Code examples** that demonstrate the issue +- **Error messages** and stack traces + +Use our bug report template: + +````markdown +## Bug Description + +[Clear description of the bug] + +## Steps to Reproduce + +1. [First step] +2. [Second step] +3. [etc.] + +## Expected Behavior + +[What you expected to happen] + +## Actual Behavior + +[What actually happened] + +## Environment + +- DebrosFramework version: [version] +- Node.js version: [version] +- OS: [operating system] + +## Code Example + +```typescript +// Minimal code example that reproduces the issue +``` +```` + +```` + +### Feature Requests + +For feature requests, please provide: + +- **Clear use case** and motivation +- **Detailed description** of the proposed feature +- **API design suggestions** (if applicable) +- **Examples** of how it would be used +- **Alternatives considered** + +### Code Contributions + +#### Before You Start + +1. **Check existing issues** to avoid duplicate work +2. **Discuss large changes** in an issue first +3. **Follow the coding standards** outlined below +4. **Write tests** for your changes +5. **Update documentation** as needed + +#### Development Workflow + +1. **Create a feature branch** + ```bash + git checkout -b feature/your-feature-name +```` + +2. **Make your changes** + + - Write clean, well-documented code + - Follow TypeScript best practices + - Add tests for new functionality + - Update relevant documentation + +3. **Test your changes** + + ```bash + pnpm run test:unit + pnpm run test:real + pnpm run lint + ``` + +4. **Commit your changes** + + ```bash + git add . + git commit -m "feat: add new feature description" + ``` + +5. **Push and create PR** + ```bash + git push origin feature/your-feature-name + ``` + +#### Commit Message Format + +We use conventional commits for consistent commit messages: + +``` +type(scope): description + +body (optional) + +footer (optional) +``` + +**Types:** + +- `feat`: New features +- `fix`: Bug fixes +- `docs`: Documentation changes +- `style`: Code style changes (formatting, etc.) +- `refactor`: Code refactoring +- `test`: Adding or updating tests +- `chore`: Maintenance tasks + +**Examples:** + +``` +feat(models): add support for computed fields +fix(query): resolve relationship loading issue +docs(readme): update installation instructions +``` + +#### Code Style Guidelines + +1. **TypeScript** + + - Use strict TypeScript configuration + - Provide proper type annotations + - Use interfaces for object types + - Follow naming conventions + +2. **Formatting** + + - Use Prettier for code formatting + - Run `pnpm run format` before committing + - Use 2 spaces for indentation + +3. **ESLint** + + - Follow ESLint rules + - Run `pnpm run lint` and fix any issues + - Use `pnpm run lint:fix` for auto-fixes + +4. **Documentation** + - Add JSDoc comments for public APIs + - Update relevant documentation files + - Include code examples where appropriate + +#### Testing Guidelines + +1. **Unit Tests** + + - Write tests for all new functionality + - Use Jest for unit testing + - Aim for high code coverage + - Test edge cases and error conditions + +2. **Integration Tests** + + - Add integration tests for significant features + - Test real-world scenarios + - Use the blog scenario tests as reference + +3. **Test Structure** + + ```typescript + describe('FeatureName', () => { + beforeEach(() => { + // Setup + }); + + it('should behave correctly in normal case', () => { + // Test implementation + }); + + it('should handle edge case', () => { + // Edge case test + }); + + it('should throw error for invalid input', () => { + // Error case test + }); + }); + ``` + +## Documentation + +### Documentation Structure + +- **README.md** - Overview and quick start +- **docs/docs/intro.md** - Framework introduction +- **docs/docs/getting-started.md** - Setup guide +- **docs/docs/core-concepts/** - Architecture and concepts +- **docs/docs/api/** - API reference +- **docs/docs/examples/** - Usage examples + +### Writing Documentation + +1. **Use clear, concise language** +2. **Provide code examples** +3. **Include both basic and advanced usage** +4. **Keep examples up-to-date** +5. **Add diagrams where helpful** + +### Building Documentation + +```bash +cd docs +npm install +npm run start # Development server +npm run build # Production build +``` + +## Release Process + +Releases are managed by the core team and follow semantic versioning: + +- **Patch** (0.5.1): Bug fixes and small improvements +- **Minor** (0.6.0): New features, backward compatible +- **Major** (1.0.0): Breaking changes + +## Community Guidelines + +### Code of Conduct + +We are committed to providing a welcoming and inclusive environment. Please: + +- **Be respectful** and considerate +- **Use inclusive language** +- **Accept constructive feedback** +- **Focus on what's best** for the community +- **Show empathy** towards other contributors + +### Getting Help + +- **GitHub Issues** - For bug reports and feature requests +- **GitHub Discussions** - For questions and community discussion +- **Discord** - For real-time chat and support + +## Recognition + +Contributors will be recognized in: + +- **CONTRIBUTORS.md** file +- **Release notes** for significant contributions +- **Documentation credits** for doc contributions + +## Questions? + +If you have questions about contributing, please: + +1. Check existing documentation +2. Search GitHub issues +3. Ask in GitHub Discussions +4. Contact the maintainers + +Thank you for contributing to DebrosFramework! ๐Ÿš€ diff --git a/README.md b/README.md index 561a8f2..314c188 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,18 @@ # @debros/network -Core networking functionality for the Debros decentralized network. This package provides a powerful database interface with advanced features built on IPFS and OrbitDB for decentralized applications. +**DebrosFramework** - A powerful Node.js framework that provides an ORM-like abstraction over OrbitDB and IPFS, making it easy to build scalable decentralized applications. -## Features +## What is DebrosFramework? -- Rich database-like API with TypeScript support -- Multiple database store types (KeyValue, Document, Feed, Counter) -- Document operations with schema validation -- Advanced querying with pagination, sorting and filtering -- Transaction support for batch operations -- Built-in file storage with metadata -- Real-time subscriptions for data changes -- Memory caching for performance -- Connection pooling for managing multiple database instances -- Index creation for faster queries -- Comprehensive error handling with error codes -- Performance metrics and monitoring +DebrosFramework simplifies the development of decentralized applications by providing: + +- **Model-based Abstraction**: Define your data models using decorators and TypeScript classes +- **Automatic Database Management**: Handle user-scoped and global databases automatically +- **Smart Sharding**: Distribute data across multiple databases for scalability +- **Advanced Query System**: Rich query capabilities with relationship loading and caching +- **Automatic Features**: Built-in pinning strategies and PubSub event publishing +- **Migration System**: Schema evolution and data transformation capabilities +- **Type Safety**: Full TypeScript support with strong typing throughout ## Installation @@ -23,280 +20,366 @@ Core networking functionality for the Debros decentralized network. This package npm install @debros/network ``` -## Basic Usage +## Quick Start + +### 1. Define Your Models ```typescript -import { initDB, create, get, query, uploadFile, logger } from '@debros/network'; +import { BaseModel, Model, Field, HasMany } from '@debros/network'; + +@Model({ + scope: 'global', + type: 'docstore', + sharding: { strategy: 'hash', count: 4, key: 'id' }, +}) +export class User extends BaseModel { + @Field({ type: 'string', required: true, unique: true }) + username: string; + + @Field({ type: 'string', required: true, unique: true }) + email: string; + + @HasMany(() => Post, 'userId') + posts: Post[]; +} + +@Model({ + scope: 'user', + type: 'docstore', + sharding: { strategy: 'user', count: 2, key: 'userId' }, +}) +export class Post extends BaseModel { + @Field({ type: 'string', required: true }) + title: string; + + @Field({ type: 'string', required: true }) + content: string; + + @Field({ type: 'string', required: true }) + userId: string; +} +``` + +### 2. Initialize the Framework + +```typescript +import { DebrosFramework } from '@debros/network'; +import { setupOrbitDB, setupIPFS } from './services'; -// Initialize the database service async function startApp() { - try { - // Initialize with default configuration - await initDB(); - logger.info('Database initialized successfully'); + // Initialize services + const orbitDBService = await setupOrbitDB(); + const ipfsService = await setupIPFS(); - // Create a new user document - const userId = 'user123'; - const user = { - username: 'johndoe', - walletAddress: '0x1234567890', - avatar: null, - }; + // Initialize framework + const framework = new DebrosFramework({ + features: { + queryCache: true, + automaticPinning: true, + pubsub: true, + }, + }); - const result = await create('users', userId, user); - logger.info(`Created user with ID: ${result.id}`); + await framework.initialize(orbitDBService, ipfsService); + console.log('โœ… DebrosFramework initialized successfully!'); - // Get a user by ID - const retrievedUser = await get('users', userId); - logger.info('User:', retrievedUser); + // Create a user + const user = await User.create({ + username: 'alice', + email: 'alice@example.com', + }); - // Query users with filtering - const activeUsers = await query('users', (user) => user.isActive === true, { - limit: 10, - sort: { field: 'createdAt', order: 'desc' }, - }); - logger.info(`Found ${activeUsers.total} active users`); + // Create a post + const post = await Post.create({ + title: 'My First Post', + content: 'Hello DebrosFramework!', + userId: user.id, + }); - // Upload a file - const fileData = Buffer.from('File content'); - const fileUpload = await uploadFile(fileData, { filename: 'document.txt' }); - logger.info(`Uploaded file with CID: ${fileUpload.cid}`); + // Query with relationships + const usersWithPosts = await User.query().with(['posts']).where('username', 'alice').find(); - return true; - } catch (error) { - logger.error('Failed to start app:', error); - throw error; + console.log('User:', usersWithPosts[0]); + console.log('Posts:', usersWithPosts[0].posts); +} +``` + +## Key Features + +### ๐Ÿ—๏ธ Model-Driven Development + +Define your data models using familiar decorator patterns: + +```typescript +@Model({ + scope: 'user', + type: 'docstore', + sharding: { strategy: 'hash', count: 4, key: 'userId' }, +}) +class Post extends BaseModel { + @Field({ type: 'string', required: true }) + title: string; + + @BelongsTo(() => User, 'userId') + user: User; +} +``` + +### ๐Ÿ” Powerful Query System + +Build complex queries with relationship loading: + +```typescript +const users = await User.query() + .where('isActive', true) + .where('registeredAt', '>', Date.now() - 30 * 24 * 60 * 60 * 1000) + .with(['posts', 'followers']) + .orderBy('username') + .limit(20) + .find(); +``` + +### ๐Ÿš€ Automatic Scaling + +Handle millions of users with automatic sharding and pinning: + +```typescript +// Framework automatically: +// - Creates user-scoped databases +// - Distributes data across shards +// - Manages pinning strategies +// - Optimizes query routing +``` + +### ๐Ÿ”„ Schema Evolution + +Migrate your data structures safely: + +```typescript +const migration = createMigration('add_user_profiles', '1.1.0') + .addField('User', 'profilePicture', { type: 'string', required: false }) + .addField('User', 'bio', { type: 'string', required: false }) + .transformData('User', (user) => ({ + ...user, + displayName: user.username || 'Anonymous', + })) + .build(); +``` + +### ๐Ÿ”— Rich Relationships + +Handle complex relationships between models: + +```typescript +@Model({ scope: 'global' }) +class User extends BaseModel { + @HasMany(() => Post, 'userId') + posts: Post[]; + + @ManyToMany(() => User, 'followers', 'following') + followers: User[]; +} + +// Load user with all relationships +const user = await User.findById(userId, { + with: ['posts.comments', 'followers.posts'], +}); +``` + +### โšก Performance Features + +```typescript +// Query caching +const cachedUsers = await User.query() + .where('isActive', true) + .cache(300) // Cache for 5 minutes + .find(); + +// Eager loading +const usersWithPosts = await User.query().with(['posts.comments']).find(); + +// Optimized pagination +const page = await User.query().orderBy('createdAt', 'desc').paginate(1, 20); +``` + +### ๐ŸŽฏ Model Hooks + +```typescript +export class User extends BaseModel { + @BeforeCreate() + async beforeCreate() { + this.createdAt = Date.now(); + // Hash password, validate data, etc. + } + + @AfterCreate() + async afterCreate() { + // Send welcome email, create defaults, etc. } } - -startApp(); -``` - -## Database Store Types - -The library supports multiple OrbitDB store types, each optimized for different use cases: - -```typescript -import { create, get, update, StoreType } from '@debros/network'; - -// Default KeyValue store (for general use) -await create('users', 'user1', { name: 'Alice' }); - -// Document store (better for complex documents with indexing) -await create( - 'posts', - 'post1', - { title: 'Hello', content: '...' }, - { storeType: StoreType.DOCSTORE }, -); - -// Feed/EventLog store (append-only, good for immutable logs) -await create('events', 'evt1', { type: 'login', user: 'alice' }, { storeType: StoreType.FEED }); - -// Counter store (for numeric counters) -await create('stats', 'visits', { value: 0 }, { storeType: StoreType.COUNTER }); - -// Increment a counter -await update('stats', 'visits', { increment: 1 }, { storeType: StoreType.COUNTER }); - -// Get counter value -const stats = await get('stats', 'visits', { storeType: StoreType.COUNTER }); -console.log(`Visit count: ${stats.value}`); -``` - -## Advanced Features - -### Schema Validation - -```typescript -import { defineSchema, create } from '@debros/network'; - -// Define a schema -defineSchema('users', { - properties: { - username: { - type: 'string', - required: true, - min: 3, - max: 20, - }, - email: { - type: 'string', - pattern: '^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$', - }, - age: { - type: 'number', - min: 18, - }, - }, - required: ['username'], -}); - -// Document creation will be validated against the schema -await create('users', 'user1', { - username: 'alice', - email: 'alice@example.com', - age: 25, -}); -``` - -### Transactions - -```typescript -import { createTransaction, commitTransaction } from '@debros/network'; - -// Create a transaction -const transaction = createTransaction(); - -// Add multiple operations -transaction - .create('posts', 'post1', { title: 'Hello World', content: '...' }) - .update('users', 'user1', { postCount: 1 }) - .delete('drafts', 'draft1'); - -// Commit all operations -const result = await commitTransaction(transaction); -console.log(`Transaction completed with ${result.results.length} operations`); -``` - -### Event Subscriptions - -```typescript -import { subscribe } from '@debros/network'; - -// Subscribe to document changes -const unsubscribe = subscribe('document:created', (data) => { - console.log(`New document created in ${data.collection}:`, data.id); - console.log('Document data:', data.document); -}); - -// Other event types -// subscribe('document:updated', (data) => { ... }); -// subscribe('document:deleted', (data) => { ... }); - -// Later, unsubscribe when done -unsubscribe(); -``` - -### Pagination and Sorting - -```typescript -import { list, query } from '@debros/network'; - -// List with pagination and sorting -const page1 = await list('users', { - limit: 10, - offset: 0, - sort: { field: 'createdAt', order: 'desc' }, -}); - -// Query with pagination -const results = await query('users', (user) => user.age > 21, { limit: 10, offset: 20 }); - -console.log(`Found ${results.total} matches, showing ${results.documents.length}`); -console.log(`Has more pages: ${results.hasMore}`); -``` - -### TypeScript Support - -```typescript -import { get, update, query } from '@debros/network'; - -interface User { - username: string; - email: string; - age: number; - createdAt: number; - updatedAt: number; -} - -// Type-safe operations -const user = await get('users', 'user1'); - -await update('users', 'user1', { age: 26 }); - -const results = await query('users', (user) => user.age > 21); -``` - -### Connection Management - -```typescript -import { initDB, closeConnection } from '@debros/network'; - -// Create multiple connections -const conn1 = await initDB('connection1'); -const conn2 = await initDB('connection2'); - -// Use specific connection -await create('users', 'user1', { name: 'Alice' }, { connectionId: conn1 }); - -// Close a specific connection -await closeConnection(conn1); ``` ## API Reference -### Core Database Operations +### Framework Management -- `initDB(connectionId?: string): Promise` - Initialize the database -- `create(collection, id, data, options?): Promise` - Create a document -- `get(collection, id, options?): Promise` - Get a document by ID -- `update(collection, id, data, options?): Promise` - Update a document -- `remove(collection, id, options?): Promise` - Delete a document -- `list(collection, options?): Promise>` - List documents with pagination -- `query(collection, filter, options?): Promise>` - Query documents -- `stopDB(): Promise` - Stop the database service +- `new DebrosFramework(config?)` - Create framework instance +- `framework.initialize(orbitDBService, ipfsService, config?)` - Initialize framework +- `framework.start()` - Start the framework +- `framework.stop()` - Stop the framework +- `framework.getStatus()` - Get framework status -### Store Types +### Model Operations -- `StoreType.KEYVALUE` - Key-value pair storage (default) -- `StoreType.DOCSTORE` - Document storage with indexing -- `StoreType.FEED` - Append-only log -- `StoreType.EVENTLOG` - Alias for FEED -- `StoreType.COUNTER` - Numeric counter +- `Model.create(data)` - Create a new model instance +- `Model.findById(id, options?)` - Find model by ID +- `Model.findOne(criteria, options?)` - Find single model +- `Model.query()` - Start a query builder +- `model.save()` - Save model changes +- `model.delete()` - Delete model instance -### Schema Validation +### Query System -- `defineSchema(collection, schema): void` - Define a schema for a collection +- `Model.query().where(field, operator, value)` - Add where condition +- `Model.query().with(relationships)` - Eager load relationships +- `Model.query().orderBy(field, direction)` - Add ordering +- `Model.query().limit(count)` - Limit results +- `Model.query().offset(count)` - Add offset +- `Model.query().paginate(page, perPage)` - Paginate results +- `Model.query().cache(ttl)` - Cache query results +- `Model.query().find()` - Execute query +- `Model.query().count()` - Count results -### Transactions +### Decorators -- `createTransaction(connectionId?): Transaction` - Create a new transaction -- `commitTransaction(transaction): Promise<{success, results}>` - Execute the transaction -- `Transaction.create(collection, id, data): Transaction` - Add a create operation -- `Transaction.update(collection, id, data): Transaction` - Add an update operation -- `Transaction.delete(collection, id): Transaction` - Add a delete operation +- `@Model(config)` - Define model configuration +- `@Field(config)` - Define field properties +- `@BelongsTo(target, foreignKey)` - Many-to-one relationship +- `@HasMany(target, foreignKey)` - One-to-many relationship +- `@HasOne(target, foreignKey)` - One-to-one relationship +- `@ManyToMany(target, through)` - Many-to-many relationship +- `@BeforeCreate()`, `@AfterCreate()` - Lifecycle hooks +- `@BeforeUpdate()`, `@AfterUpdate()` - Update hooks +- `@BeforeDelete()`, `@AfterDelete()` - Delete hooks -### Event Subscriptions +### Migration System -- `subscribe(event, callback): () => void` - Subscribe to database events, returns unsubscribe function -- Event types: 'document:created', 'document:updated', 'document:deleted' - -### File Operations - -- `uploadFile(fileData, options?): Promise` - Upload a file -- `getFile(cid, options?): Promise` - Get a file by CID -- `deleteFile(cid, options?): Promise` - Delete a file - -### Connection Management - -- `closeConnection(connectionId): Promise` - Close a specific connection - -### Indexes and Performance - -- `createIndex(collection, field, options?): Promise` - Create an index +- `createMigration(name, version)` - Create new migration +- `migration.addField(model, field, config)` - Add field to model +- `migration.removeField(model, field)` - Remove field from model +- `migration.transformData(model, transformer)` - Transform existing data +- `migrationManager.runPendingMigrations()` - Run pending migrations ## Configuration +### Framework Configuration + ```typescript -import { config, initDB } from '@debros/network'; +import { DebrosFramework, PRODUCTION_CONFIG, DEVELOPMENT_CONFIG } from '@debros/network'; -// Configure (optional) -config.env.fingerprint = 'my-unique-app-id'; -config.env.port = 9000; -config.ipfs.blockstorePath = './custom-path/blockstore'; -config.orbitdb.directory = './custom-path/orbitdb'; +// Development configuration +const framework = new DebrosFramework({ + ...DEVELOPMENT_CONFIG, + features: { + queryCache: true, + automaticPinning: false, + pubsub: true, + relationshipCache: true, + autoMigration: true, + }, + performance: { + queryTimeout: 30000, + batchSize: 50, + }, + monitoring: { + enableMetrics: true, + logLevel: 'debug', + }, +}); -// Initialize with configuration -await initDB(); +// Production configuration +const prodFramework = new DebrosFramework({ + ...PRODUCTION_CONFIG, + performance: { + queryTimeout: 10000, + batchSize: 200, + maxConcurrentOperations: 500, + }, +}); ``` + +### Model Configuration + +```typescript +@Model({ + scope: 'global', // 'user' or 'global' + type: 'docstore', // OrbitDB store type + sharding: { + strategy: 'hash', // 'hash', 'range', or 'user' + count: 4, // Number of shards + key: 'id', // Sharding key + }, + pinning: { + strategy: 'popularity', // Pinning strategy + factor: 2, + }, +}) +export class MyModel extends BaseModel { + // Model definition +} +``` + +## Architecture Overview + +DebrosFramework is built around several core components: + +1. **Models & Decorators**: Define your data structure and behavior +2. **Database Manager**: Handles database creation and management +3. **Shard Manager**: Distributes data across multiple databases +4. **Query System**: Processes queries with optimization and caching +5. **Relationship Manager**: Handles complex relationships between models +6. **Migration System**: Manages schema evolution over time +7. **Automatic Features**: Pinning, PubSub, and performance optimization + +### Key Benefits + +- **Scalability**: Automatic sharding and distributed data management +- **Performance**: Built-in caching, query optimization, and lazy loading +- **Developer Experience**: Familiar ORM patterns with TypeScript support +- **Flexibility**: Support for various data patterns and relationships +- **Reliability**: Comprehensive error handling and recovery mechanisms + +## Getting Started + +Ready to build your first decentralized application? Check out our comprehensive documentation: + +- **[๐Ÿ“– Complete Documentation](./docs)** - Comprehensive guides and examples +- **[๐Ÿš€ Getting Started Guide](./docs/docs/getting-started.md)** - Set up your development environment +- **[๐Ÿ—๏ธ Architecture Overview](./docs/docs/core-concepts/architecture.md)** - Understand how the framework works +- **[๐Ÿ“ API Reference](./docs/docs/api/overview.md)** - Complete API documentation +- **[๐Ÿ’ก Examples](./docs/docs/examples/basic-usage.md)** - Practical usage examples + +## Testing + +```bash +# Run unit tests +npm run test:unit + +# Run integration tests +npm run test:real + +# Run specific blog scenario integration test +npm run test:blog-integration +``` + +## Contributing + +We welcome contributions! Please see our [Contributing Guide](./CONTRIBUTING.md) for details. + +## License + +This project is licensed under the GNU GPL v3.0 License - see the [LICENSE](./LICENSE) file for details. + +--- + +**DebrosFramework** - Making decentralized application development as simple as traditional web development, while providing the benefits of distributed systems. diff --git a/docs/docs/api/base-model.md b/docs/docs/api/base-model.md new file mode 100644 index 0000000..c86735e --- /dev/null +++ b/docs/docs/api/base-model.md @@ -0,0 +1,810 @@ +--- +sidebar_position: 3 +--- + +# BaseModel Class + +The `BaseModel` class is the abstract base class for all data models in Debros Network. It provides ORM-like functionality with automatic database management, validation, relationships, and lifecycle hooks. + +## Class Definition + +```typescript +abstract class BaseModel { + // Instance properties + id: string; + createdAt?: number; + updatedAt?: number; + + // Static methods + static async create( + this: ModelConstructor, + data: Partial, + options?: CreateOptions, + ): Promise; + + static async findById( + this: ModelConstructor, + id: string, + options?: FindOptions, + ): Promise; + + static async findOne( + this: ModelConstructor, + criteria: Partial, + options?: FindOptions, + ): Promise; + + static query(this: ModelConstructor): QueryBuilder; + + // Instance methods + save(options?: SaveOptions): Promise; + delete(options?: DeleteOptions): Promise; + reload(options?: ReloadOptions): Promise; + validate(): Promise; + toJSON(): Record; + clone(): this; +} +``` + +## Static Methods + +### create(data, options?) + +Creates a new model instance and saves it to the database. + +**Parameters:** + +- `data`: Partial model data +- `options` (optional): Creation options + +**Returns:** `Promise` - The created model instance + +**Throws:** + +- `ValidationError` - If validation fails +- `DatabaseError` - If database operation fails + +**Example:** + +```typescript +import { BaseModel, Model, Field } from '@debros/network'; + +@Model({ + scope: 'global', + type: 'docstore', +}) +class User extends BaseModel { + @Field({ type: 'string', required: true, unique: true }) + username: string; + + @Field({ type: 'string', required: true, unique: true }) + email: string; + + @Field({ type: 'number', required: false, default: 0 }) + score: number; +} + +// Create a new user +const user = await User.create({ + username: 'alice', + email: 'alice@example.com', + score: 100, +}); + +console.log(user.id); // Generated ID +console.log(user.username); // 'alice' +console.log(user.createdAt); // Timestamp +``` + +**With validation:** + +```typescript +try { + const user = await User.create({ + username: 'ab', // Too short + email: 'invalid-email', // Invalid format + }); +} catch (error) { + if (error instanceof ValidationError) { + console.log('Validation failed:', error.field, error.constraint); + } +} +``` + +**With options:** + +```typescript +const user = await User.create( + { + username: 'bob', + email: 'bob@example.com', + }, + { + validate: true, + skipHooks: false, + userId: 'user123', // For user-scoped models + }, +); +``` + +### findById(id, options?) + +Finds a model instance by its ID. + +**Parameters:** + +- `id`: The model ID to search for +- `options` (optional): Find options + +**Returns:** `Promise` - The found model or null + +**Example:** + +```typescript +// Basic find +const user = await User.findById('user123'); +if (user) { + console.log('Found user:', user.username); +} else { + console.log('User not found'); +} + +// With relationships +const user = await User.findById('user123', { + with: ['posts', 'profile'], +}); + +// With specific user context +const user = await User.findById('user123', { + userId: 'current-user-id', +}); +``` + +### findOne(criteria, options?) + +Finds the first model instance matching the criteria. + +**Parameters:** + +- `criteria`: Partial model data to match +- `options` (optional): Find options + +**Returns:** `Promise` - The found model or null + +**Example:** + +```typescript +// Find by field +const user = await User.findOne({ + username: 'alice', +}); + +// Find with multiple criteria +const user = await User.findOne({ + email: 'alice@example.com', + isActive: true, +}); + +// With options +const user = await User.findOne( + { + username: 'alice', + }, + { + with: ['posts'], + cache: 300, // Cache for 5 minutes + }, +); +``` + +### query() + +Returns a query builder for complex queries. + +**Returns:** `QueryBuilder` - Query builder instance + +**Example:** + +```typescript +// Basic query +const users = await User.query() + .where('isActive', true) + .orderBy('createdAt', 'desc') + .limit(10) + .find(); + +// Complex query with relationships +const activeUsers = await User.query() + .where('isActive', true) + .where('score', '>', 100) + .where('registeredAt', '>', Date.now() - 30 * 24 * 60 * 60 * 1000) + .with(['posts.comments', 'profile']) + .orderBy('score', 'desc') + .paginate(1, 20); + +// Query with caching +const cachedUsers = await User.query() + .where('isActive', true) + .cache(600) // Cache for 10 minutes + .find(); +``` + +## Instance Methods + +### save(options?) + +Saves the current model instance to the database. + +**Parameters:** + +- `options` (optional): Save options + +**Returns:** `Promise` - The saved model instance + +**Example:** + +```typescript +const user = await User.findById('user123'); +user.email = 'newemail@example.com'; +user.score += 10; + +await user.save(); +console.log('User updated'); + +// With options +await user.save({ + validate: true, + skipHooks: false, +}); +``` + +### delete(options?) + +Deletes the model instance from the database. + +**Parameters:** + +- `options` (optional): Delete options + +**Returns:** `Promise` - True if deletion was successful + +**Example:** + +```typescript +const user = await User.findById('user123'); +if (user) { + const deleted = await user.delete(); + console.log('User deleted:', deleted); +} + +// With options +await user.delete({ + skipHooks: false, + cascade: true, // Delete related records +}); +``` + +### reload(options?) + +Reloads the model instance from the database. + +**Parameters:** + +- `options` (optional): Reload options + +**Returns:** `Promise` - The reloaded model instance + +**Example:** + +```typescript +const user = await User.findById('user123'); + +// Model might be updated elsewhere +await user.reload(); + +// With relationships +await user.reload({ + with: ['posts', 'profile'], +}); +``` + +### validate() + +Validates the current model instance. + +**Returns:** `Promise` - Validation result + +**Example:** + +```typescript +const user = new User(); +user.username = 'alice'; +user.email = 'invalid-email'; + +const result = await user.validate(); +if (!result.valid) { + console.log('Validation errors:', result.errors); + // [{ field: 'email', constraint: 'must be valid email', value: 'invalid-email' }] +} +``` + +### toJSON() + +Converts the model instance to a plain JavaScript object. + +**Returns:** `Record` - Plain object representation + +**Example:** + +```typescript +const user = await User.findById('user123'); +const userObj = user.toJSON(); + +console.log(userObj); +// { +// id: 'user123', +// username: 'alice', +// email: 'alice@example.com', +// score: 100, +// createdAt: 1234567890, +// updatedAt: 1234567890 +// } + +// Useful for JSON serialization +const jsonString = JSON.stringify(user); // Calls toJSON() automatically +``` + +### clone() + +Creates a deep copy of the model instance (without ID). + +**Returns:** `this` - Cloned model instance + +**Example:** + +```typescript +const user = await User.findById('user123'); +const userCopy = user.clone(); + +userCopy.username = 'alice_copy'; +await userCopy.save(); // Creates new record + +console.log(user.id !== userCopy.id); // true +``` + +## Lifecycle Hooks + +### Hook Decorators + +Models can define lifecycle hooks using decorators: + +```typescript +@Model({ scope: 'global', type: 'docstore' }) +class User extends BaseModel { + @Field({ type: 'string', required: true }) + username: string; + + @Field({ type: 'string', required: true }) + email: string; + + @Field({ type: 'number', required: false }) + loginCount: number = 0; + + @BeforeCreate() + async beforeCreateHook() { + this.createdAt = Date.now(); + this.updatedAt = Date.now(); + + // Validate unique username + const existing = await User.findOne({ username: this.username }); + if (existing) { + throw new ValidationError('username', this.username, 'must be unique'); + } + } + + @AfterCreate() + async afterCreateHook() { + console.log(`New user created: ${this.username}`); + + // Send welcome email + await this.sendWelcomeEmail(); + + // Create default settings + await this.createDefaultSettings(); + } + + @BeforeUpdate() + async beforeUpdateHook() { + this.updatedAt = Date.now(); + } + + @AfterUpdate() + async afterUpdateHook() { + console.log(`User updated: ${this.username}`); + } + + @BeforeDelete() + async beforeDeleteHook() { + // Clean up related data + await this.deleteRelatedPosts(); + } + + @AfterDelete() + async afterDeleteHook() { + console.log(`User deleted: ${this.username}`); + } + + // Custom methods + private async sendWelcomeEmail() { + // Implementation + } + + private async createDefaultSettings() { + // Implementation + } + + private async deleteRelatedPosts() { + // Implementation + } +} +``` + +### Available Hook Types + +| Hook | Trigger | Usage | +| ----------------- | ------------------------------- | --------------------------------------- | +| `@BeforeCreate()` | Before creating new record | Validation, default values, preparation | +| `@AfterCreate()` | After creating new record | Notifications, related record creation | +| `@BeforeUpdate()` | Before updating existing record | Validation, timestamp updates, logging | +| `@AfterUpdate()` | After updating existing record | Cache invalidation, notifications | +| `@BeforeDelete()` | Before deleting record | Cleanup, validation, logging | +| `@AfterDelete()` | After deleting record | Cleanup, notifications, cascade deletes | +| `@BeforeSave()` | Before save (create or update) | Common validation, timestamps | +| `@AfterSave()` | After save (create or update) | Common post-processing | + +## Validation + +### Field Validation + +```typescript +@Model({ scope: 'global', type: 'docstore' }) +class User extends BaseModel { + @Field({ + type: 'string', + required: true, + unique: true, + minLength: 3, + maxLength: 20, + validate: (username: string) => /^[a-zA-Z0-9_]+$/.test(username), + transform: (username: string) => username.toLowerCase(), + }) + username: string; + + @Field({ + type: 'string', + required: true, + unique: true, + validate: (email: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email), + transform: (email: string) => email.toLowerCase(), + }) + email: string; + + @Field({ + type: 'number', + required: false, + default: 0, + validate: (score: number) => score >= 0 && score <= 1000, + }) + score: number; + + @Field({ + type: 'array', + required: false, + default: [], + validate: (tags: string[]) => tags.length <= 10, + }) + tags: string[]; +} +``` + +### Custom Validation Methods + +```typescript +@Model({ scope: 'global', type: 'docstore' }) +class User extends BaseModel { + @Field({ type: 'string', required: true }) + username: string; + + @Field({ type: 'string', required: true }) + email: string; + + @Field({ type: 'string', required: true }) + password: string; + + // Custom validation method + async customValidation(): Promise { + const errors: ValidationError[] = []; + + // Check username availability + if (this.username) { + const existing = await User.findOne({ username: this.username }); + if (existing && existing.id !== this.id) { + errors.push(new ValidationError('username', this.username, 'already taken')); + } + } + + // Check email format and availability + if (this.email) { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailRegex.test(this.email)) { + errors.push(new ValidationError('email', this.email, 'invalid format')); + } + + const existing = await User.findOne({ email: this.email }); + if (existing && existing.id !== this.id) { + errors.push(new ValidationError('email', this.email, 'already registered')); + } + } + + // Check password strength + if (this.password && this.password.length < 8) { + errors.push(new ValidationError('password', this.password, 'must be at least 8 characters')); + } + + return { + valid: errors.length === 0, + errors, + }; + } + + @BeforeCreate() + @BeforeUpdate() + async validateBeforeSave() { + const result = await this.customValidation(); + if (!result.valid) { + throw new ValidationError('model', this, 'custom validation failed', result.errors); + } + } +} +``` + +## Configuration Interfaces + +### ModelConstructor Type + +```typescript +type ModelConstructor = new () => T; +``` + +### Options Interfaces + +```typescript +interface CreateOptions { + validate?: boolean; + skipHooks?: boolean; + userId?: string; // For user-scoped models +} + +interface FindOptions { + with?: string[]; // Relationship names to eager load + cache?: boolean | number; // Cache result + userId?: string; // For user-scoped models +} + +interface SaveOptions { + validate?: boolean; + skipHooks?: boolean; + upsert?: boolean; +} + +interface DeleteOptions { + skipHooks?: boolean; + cascade?: boolean; // Delete related records +} + +interface ReloadOptions { + with?: string[]; // Relationship names to eager load +} +``` + +### Validation Types + +```typescript +interface ValidationResult { + valid: boolean; + errors: ValidationError[]; +} + +class ValidationError extends Error { + constructor( + public field: string, + public value: any, + public constraint: string, + public details?: any, + ) { + super(`Validation failed for field '${field}': ${constraint}`); + } +} +``` + +## Error Handling + +### Common Errors + +```typescript +// Validation errors +try { + const user = await User.create({ + username: 'a', // Too short + email: 'invalid', + }); +} catch (error) { + if (error instanceof ValidationError) { + console.log(`Field ${error.field} failed: ${error.constraint}`); + } +} + +// Not found errors +const user = await User.findById('non-existent-id'); +if (!user) { + throw new NotFoundError('User', 'non-existent-id'); +} + +// Database errors +try { + await user.save(); +} catch (error) { + if (error instanceof DatabaseError) { + console.log('Database operation failed:', error.message); + } +} +``` + +## Complete Example + +### Blog Post Model + +```typescript +import { + BaseModel, + Model, + Field, + BelongsTo, + HasMany, + BeforeCreate, + AfterCreate, +} from '@debros/network'; +import { User } from './User'; +import { Comment } from './Comment'; + +@Model({ + scope: 'user', + type: 'docstore', + sharding: { + strategy: 'user', + count: 2, + key: 'authorId', + }, +}) +export class Post extends BaseModel { + @Field({ + type: 'string', + required: true, + minLength: 1, + maxLength: 200, + }) + title: string; + + @Field({ + type: 'string', + required: true, + minLength: 1, + maxLength: 10000, + }) + content: string; + + @Field({ type: 'string', required: true }) + authorId: string; + + @Field({ + type: 'array', + required: false, + default: [], + transform: (tags: string[]) => tags.map((tag) => tag.toLowerCase()), + }) + tags: string[]; + + @Field({ type: 'boolean', required: false, default: false }) + isPublished: boolean; + + @Field({ type: 'number', required: false, default: 0 }) + viewCount: number; + + @Field({ type: 'number', required: false }) + publishedAt?: number; + + // Relationships + @BelongsTo(() => User, 'authorId') + author: User; + + @HasMany(() => Comment, 'postId') + comments: Comment[]; + + @BeforeCreate() + setupNewPost() { + this.createdAt = Date.now(); + this.updatedAt = Date.now(); + this.viewCount = 0; + } + + @AfterCreate() + async afterPostCreated() { + console.log(`New post created: ${this.title}`); + + // Update author's post count + const author = await User.findById(this.authorId); + if (author) { + author.postCount = (author.postCount || 0) + 1; + await author.save(); + } + } + + // Custom methods + async publish(): Promise { + this.isPublished = true; + this.publishedAt = Date.now(); + await this.save(); + } + + async incrementViews(): Promise { + this.viewCount += 1; + await this.save({ skipHooks: true }); // Skip hooks for performance + } + + async getCommentCount(): Promise { + return await Comment.query().where('postId', this.id).count(); + } + + async getTopComments(limit: number = 5): Promise { + return await Comment.query() + .where('postId', this.id) + .orderBy('likeCount', 'desc') + .limit(limit) + .with(['author']) + .find(); + } +} + +// Usage examples +async function blogExamples() { + // Create a post + const post = await Post.create({ + title: 'My First Post', + content: 'This is the content of my first post...', + authorId: 'user123', + tags: ['JavaScript', 'Web Development'], + }); + + // Find posts by author + const userPosts = await Post.query() + .where('authorId', 'user123') + .where('isPublished', true) + .orderBy('publishedAt', 'desc') + .with(['author', 'comments.author']) + .find(); + + // Publish a post + await post.publish(); + + // Get post with comments + const postWithComments = await Post.findById(post.id, { + with: ['author', 'comments.author'], + }); + + console.log('Post:', postWithComments?.title); + console.log('Author:', postWithComments?.author.username); + console.log('Comments:', postWithComments?.comments.length); +} +``` + +This comprehensive BaseModel documentation covers all the essential functionality for working with models in Debros Network, including CRUD operations, validation, relationships, hooks, and real-world usage examples. diff --git a/docs/docs/api/query-builder.md b/docs/docs/api/query-builder.md new file mode 100644 index 0000000..4ed69d4 --- /dev/null +++ b/docs/docs/api/query-builder.md @@ -0,0 +1,828 @@ +--- +sidebar_position: 4 +--- + +# QueryBuilder Class + +The `QueryBuilder` class provides a fluent API for constructing complex database queries. It supports filtering, sorting, relationships, pagination, and caching with type safety throughout. + +## Class Definition + +```typescript +class QueryBuilder { + // Filtering methods + where(field: string, value: any): QueryBuilder; + where(field: string, operator: QueryOperator, value: any): QueryBuilder; + whereIn(field: string, values: any[]): QueryBuilder; + whereNotIn(field: string, values: any[]): QueryBuilder; + whereNull(field: string): QueryBuilder; + whereNotNull(field: string): QueryBuilder; + whereBetween(field: string, min: any, max: any): QueryBuilder; + whereRaw(condition: string, parameters?: any[]): QueryBuilder; + + // Logical operators + and(): QueryBuilder; + or(): QueryBuilder; + not(): QueryBuilder; + + // Sorting + orderBy(field: string, direction?: 'asc' | 'desc'): QueryBuilder; + orderByRaw(orderClause: string): QueryBuilder; + + // Limiting and pagination + limit(count: number): QueryBuilder; + offset(count: number): QueryBuilder; + paginate(page: number, perPage: number): Promise>; + + // Relationships + with(relationships: string[]): QueryBuilder; + withCount(relationships: string[]): QueryBuilder; + whereHas(relationship: string, callback?: (query: QueryBuilder) => void): QueryBuilder; + whereDoesntHave(relationship: string): QueryBuilder; + + // Aggregation + count(): Promise; + sum(field: string): Promise; + avg(field: string): Promise; + min(field: string): Promise; + max(field: string): Promise; + + // Caching + cache(ttl?: number): QueryBuilder; + fresh(): QueryBuilder; + + // Execution + find(): Promise; + findOne(): Promise; + first(): Promise; + get(): Promise; + + // Advanced features + distinct(field?: string): QueryBuilder; + groupBy(field: string): QueryBuilder; + having(field: string, operator: QueryOperator, value: any): QueryBuilder; + + // Query info + toSQL(): string; + getParameters(): any[]; + explain(): Promise; +} +``` + +## Filtering Methods + +### where(field, value) / where(field, operator, value) + +Adds a WHERE condition to the query. + +**Parameters:** + +- `field`: Field name to filter on +- `value`: Value to compare (when using equals operator) +- `operator`: Comparison operator +- `value`: Value to compare against + +**Returns:** `QueryBuilder` - Builder instance for chaining + +**Example:** + +```typescript +// Basic equality +const activeUsers = await User.query().where('isActive', true).find(); + +// With operators +const highScoreUsers = await User.query() + .where('score', '>', 1000) + .where('registeredAt', '>=', Date.now() - 30 * 24 * 60 * 60 * 1000) + .find(); + +// String operations +const usersWithAlice = await User.query().where('username', 'like', '%alice%').find(); + +// Array operations +const adminUsers = await User.query().where('roles', 'includes', 'admin').find(); +``` + +### whereIn(field, values) + +Filters records where field value is in the provided array. + +**Parameters:** + +- `field`: Field name +- `values`: Array of values to match + +**Returns:** `QueryBuilder` + +**Example:** + +```typescript +// Find users with specific IDs +const users = await User.query().whereIn('id', ['user1', 'user2', 'user3']).find(); + +// Find posts with specific tags +const posts = await Post.query().whereIn('category', ['tech', 'programming', 'tutorial']).find(); +``` + +### whereNotIn(field, values) + +Filters records where field value is NOT in the provided array. + +**Parameters:** + +- `field`: Field name +- `values`: Array of values to exclude + +**Returns:** `QueryBuilder` + +**Example:** + +```typescript +// Exclude specific users +const users = await User.query().whereNotIn('status', ['banned', 'suspended']).find(); +``` + +### whereNull(field) / whereNotNull(field) + +Filters records based on null values. + +**Parameters:** + +- `field`: Field name to check + +**Returns:** `QueryBuilder` + +**Example:** + +```typescript +// Find users without profile pictures +const usersWithoutAvatars = await User.query().whereNull('avatarUrl').find(); + +// Find users with profile pictures +const usersWithAvatars = await User.query().whereNotNull('avatarUrl').find(); +``` + +### whereBetween(field, min, max) + +Filters records where field value is between min and max. + +**Parameters:** + +- `field`: Field name +- `min`: Minimum value (inclusive) +- `max`: Maximum value (inclusive) + +**Returns:** `QueryBuilder` + +**Example:** + +```typescript +// Find users with scores between 100 and 500 +const moderateUsers = await User.query().whereBetween('score', 100, 500).find(); + +// Find posts from last week +const lastWeek = Date.now() - 7 * 24 * 60 * 60 * 1000; +const recentPosts = await Post.query().whereBetween('createdAt', lastWeek, Date.now()).find(); +``` + +### whereRaw(condition, parameters?) + +Adds a raw WHERE condition. + +**Parameters:** + +- `condition`: Raw SQL-like condition string +- `parameters`: Optional parameters for the condition + +**Returns:** `QueryBuilder` + +**Example:** + +```typescript +// Complex condition +const users = await User.query() + .whereRaw('score > ? AND (registeredAt > ? OR isPremium = ?)', [100, lastWeek, true]) + .find(); +``` + +## Logical Operators + +### and() / or() / not() + +Combines conditions with logical operators. + +**Returns:** `QueryBuilder` + +**Example:** + +```typescript +// AND (default behavior) +const premiumActiveUsers = await User.query() + .where('isActive', true) + .and() + .where('isPremium', true) + .find(); + +// OR condition +const eligibleUsers = await User.query() + .where('isPremium', true) + .or() + .where('score', '>', 1000) + .find(); + +// NOT condition +const nonAdminUsers = await User.query().not().where('role', 'admin').find(); + +// Complex combinations +const complexQuery = await User.query() + .where('isActive', true) + .and() + .group((query) => query.where('isPremium', true).or().where('score', '>', 500)) + .find(); +``` + +## Sorting Methods + +### orderBy(field, direction?) + +Orders results by specified field. + +**Parameters:** + +- `field`: Field name to sort by +- `direction`: Sort direction ('asc' or 'desc', defaults to 'asc') + +**Returns:** `QueryBuilder` + +**Example:** + +```typescript +// Sort by score descending +const topUsers = await User.query().orderBy('score', 'desc').limit(10).find(); + +// Multiple sort criteria +const users = await User.query().orderBy('score', 'desc').orderBy('username', 'asc').find(); + +// Sort by date +const recentPosts = await Post.query().orderBy('createdAt', 'desc').find(); +``` + +### orderByRaw(orderClause) + +Orders results using a raw ORDER BY clause. + +**Parameters:** + +- `orderClause`: Raw order clause + +**Returns:** `QueryBuilder` + +**Example:** + +```typescript +const users = await User.query().orderByRaw('score DESC, RANDOM()').find(); +``` + +## Limiting and Pagination + +### limit(count) + +Limits the number of results. + +**Parameters:** + +- `count`: Maximum number of records to return + +**Returns:** `QueryBuilder` + +**Example:** + +```typescript +// Get top 10 users +const topUsers = await User.query().orderBy('score', 'desc').limit(10).find(); +``` + +### offset(count) + +Skips a number of records. + +**Parameters:** + +- `count`: Number of records to skip + +**Returns:** `QueryBuilder` + +**Example:** + +```typescript +// Get users 11-20 (skip first 10) +const nextBatch = await User.query().orderBy('score', 'desc').offset(10).limit(10).find(); +``` + +### paginate(page, perPage) + +Paginates results and returns pagination info. + +**Parameters:** + +- `page`: Page number (1-based) +- `perPage`: Number of records per page + +**Returns:** `Promise>` + +**Example:** + +```typescript +// Get page 2 with 20 users per page +const result = await User.query().where('isActive', true).orderBy('score', 'desc').paginate(2, 20); + +console.log('Users:', result.data); +console.log('Total:', result.total); +console.log('Page:', result.page); +console.log('Per page:', result.perPage); +console.log('Total pages:', result.totalPages); +console.log('Has next:', result.hasNext); +console.log('Has previous:', result.hasPrev); +``` + +## Relationship Methods + +### with(relationships) + +Eager loads relationships. + +**Parameters:** + +- `relationships`: Array of relationship names or dot-notation for nested relationships + +**Returns:** `QueryBuilder` + +**Example:** + +```typescript +// Load single relationship +const usersWithPosts = await User.query().with(['posts']).find(); + +// Load multiple relationships +const usersWithData = await User.query().with(['posts', 'profile', 'followers']).find(); + +// Load nested relationships +const usersWithCommentsAuthors = await User.query().with(['posts.comments.author']).find(); + +// Mixed relationships +const complexLoad = await User.query() + .with(['posts.comments.author', 'profile', 'followers.profile']) + .find(); +``` + +### withCount(relationships) + +Loads relationship counts without loading the actual relationships. + +**Parameters:** + +- `relationships`: Array of relationship names + +**Returns:** `QueryBuilder` + +**Example:** + +```typescript +const usersWithCounts = await User.query().withCount(['posts', 'followers', 'following']).find(); + +// Access counts +usersWithCounts.forEach((user) => { + console.log(`${user.username}: ${user.postsCount} posts, ${user.followersCount} followers`); +}); +``` + +### whereHas(relationship, callback?) + +Filters records that have related records matching criteria. + +**Parameters:** + +- `relationship`: Relationship name +- `callback`: Optional callback to add conditions to the relationship query + +**Returns:** `QueryBuilder` + +**Example:** + +```typescript +// Users who have posts +const usersWithPosts = await User.query().whereHas('posts').find(); + +// Users who have published posts +const usersWithPublishedPosts = await User.query() + .whereHas('posts', (query) => { + query.where('isPublished', true); + }) + .find(); + +// Users who have recent posts +const usersWithRecentPosts = await User.query() + .whereHas('posts', (query) => { + query.where('createdAt', '>', Date.now() - 7 * 24 * 60 * 60 * 1000); + }) + .find(); +``` + +### whereDoesntHave(relationship) + +Filters records that don't have related records. + +**Parameters:** + +- `relationship`: Relationship name + +**Returns:** `QueryBuilder` + +**Example:** + +```typescript +// Users without posts +const usersWithoutPosts = await User.query().whereDoesntHave('posts').find(); +``` + +## Aggregation Methods + +### count() + +Returns the count of matching records. + +**Returns:** `Promise` + +**Example:** + +```typescript +// Count all active users +const activeUserCount = await User.query().where('isActive', true).count(); + +console.log(`Active users: ${activeUserCount}`); +``` + +### sum(field) / avg(field) / min(field) / max(field) + +Performs aggregation operations on a field. + +**Parameters:** + +- `field`: Field name to aggregate + +**Returns:** `Promise` or `Promise` for min/max + +**Example:** + +```typescript +// Calculate statistics +const totalScore = await User.query().sum('score'); +const averageScore = await User.query().avg('score'); +const highestScore = await User.query().max('score'); +const lowestScore = await User.query().min('score'); + +console.log(`Total: ${totalScore}, Average: ${averageScore}`); +console.log(`Range: ${lowestScore} - ${highestScore}`); + +// With conditions +const premiumAverage = await User.query().where('isPremium', true).avg('score'); +``` + +## Caching Methods + +### cache(ttl?) + +Caches query results. + +**Parameters:** + +- `ttl`: Time to live in seconds (optional, uses default if not provided) + +**Returns:** `QueryBuilder` + +**Example:** + +```typescript +// Cache for default duration +const cachedUsers = await User.query().where('isActive', true).cache().find(); + +// Cache for 10 minutes +const cachedPosts = await Post.query().where('isPublished', true).cache(600).find(); +``` + +### fresh() + +Forces a fresh query, bypassing cache. + +**Returns:** `QueryBuilder` + +**Example:** + +```typescript +// Always fetch fresh data +const freshUsers = await User.query().where('isActive', true).fresh().find(); +``` + +## Execution Methods + +### find() + +Executes the query and returns all matching records. + +**Returns:** `Promise` + +**Example:** + +```typescript +const users = await User.query().where('isActive', true).orderBy('score', 'desc').find(); +``` + +### findOne() / first() + +Executes the query and returns the first matching record. + +**Returns:** `Promise` + +**Example:** + +```typescript +// Find the highest scoring user +const topUser = await User.query().orderBy('score', 'desc').findOne(); + +// Find specific user +const user = await User.query().where('username', 'alice').first(); +``` + +### get() + +Alias for `find()`. + +**Returns:** `Promise` + +## Advanced Methods + +### distinct(field?) + +Returns distinct values. + +**Parameters:** + +- `field`: Optional field to get distinct values for + +**Returns:** `QueryBuilder` + +**Example:** + +```typescript +// Get users with distinct emails +const uniqueUsers = await User.query().distinct('email').find(); + +// Get all distinct tags from posts +const uniqueTags = await Post.query().distinct('tags').find(); +``` + +### groupBy(field) + +Groups results by field. + +**Parameters:** + +- `field`: Field to group by + +**Returns:** `QueryBuilder` + +**Example:** + +```typescript +// Group users by registration date +const usersByDate = await User.query().groupBy('registeredAt').withCount(['posts']).find(); +``` + +### having(field, operator, value) + +Adds HAVING condition (used with groupBy). + +**Parameters:** + +- `field`: Field name +- `operator`: Comparison operator +- `value`: Value to compare + +**Returns:** `QueryBuilder` + +**Example:** + +```typescript +// Find dates with more than 10 user registrations +const busyDays = await User.query().groupBy('registeredAt').having('COUNT(*)', '>', 10).find(); +``` + +## Query Inspection + +### toSQL() + +Returns the SQL representation of the query. + +**Returns:** `string` + +**Example:** + +```typescript +const query = User.query().where('isActive', true).orderBy('score', 'desc').limit(10); + +console.log('SQL:', query.toSQL()); +``` + +### getParameters() + +Returns the parameters for the query. + +**Returns:** `any[]` + +**Example:** + +```typescript +const query = User.query().where('score', '>', 100); + +console.log('Parameters:', query.getParameters()); +``` + +### explain() + +Returns the query execution plan. + +**Returns:** `Promise` + +**Example:** + +```typescript +const plan = await User.query().where('isActive', true).explain(); + +console.log('Query plan:', plan); +``` + +## Type Definitions + +### QueryOperator + +```typescript +type QueryOperator = + | 'eq' + | '=' + | 'ne' + | '!=' + | '<>' + | 'gt' + | '>' + | 'gte' + | '>=' + | 'lt' + | '<' + | 'lte' + | '<=' + | 'in' + | 'not in' + | 'like' + | 'not like' + | 'regex' + | 'is null' + | 'is not null' + | 'includes' + | 'includes any' + | 'includes all'; +``` + +### PaginatedResult + +```typescript +interface PaginatedResult { + data: T[]; + total: number; + page: number; + perPage: number; + totalPages: number; + hasNext: boolean; + hasPrev: boolean; + firstPage: number; + lastPage: number; +} +``` + +### QueryPlan + +```typescript +interface QueryPlan { + estimatedCost: number; + estimatedRows: number; + indexesUsed: string[]; + operations: QueryOperation[]; + warnings: string[]; +} + +interface QueryOperation { + type: string; + description: string; + cost: number; +} +``` + +## Complex Query Examples + +### Advanced Filtering + +```typescript +// Complex user search +const searchResults = await User.query() + .where('isActive', true) + .and() + .group((query) => + query + .where('username', 'like', `%${searchTerm}%`) + .or() + .where('email', 'like', `%${searchTerm}%`) + .or() + .where('bio', 'like', `%${searchTerm}%`), + ) + .and() + .group((query) => query.where('isPremium', true).or().where('score', '>', 500)) + .orderBy('score', 'desc') + .with(['profile', 'posts.comments']) + .paginate(page, 20); +``` + +### Analytics Query + +```typescript +// User activity analytics +const analytics = await User.query() + .where('registeredAt', '>', startDate) + .where('registeredAt', '<', endDate) + .whereHas('posts', (query) => { + query.where('isPublished', true); + }) + .withCount(['posts', 'comments', 'followers']) + .orderBy('score', 'desc') + .cache(300) // Cache for 5 minutes + .find(); + +// Process analytics data +const stats = { + totalUsers: analytics.length, + averageScore: analytics.reduce((sum, user) => sum + user.score, 0) / analytics.length, + totalPosts: analytics.reduce((sum, user) => sum + user.postsCount, 0), + totalComments: analytics.reduce((sum, user) => sum + user.commentsCount, 0), +}; +``` + +### Dashboard Query + +```typescript +async function getDashboardData(userId: string) { + // Multiple optimized queries + const [userStats, recentPosts, topComments, followingActivity] = await Promise.all([ + // User statistics + User.query() + .where('id', userId) + .withCount(['posts', 'comments', 'followers', 'following']) + .cache(60) // Cache for 1 minute + .first(), + + // Recent posts + Post.query() + .where('authorId', userId) + .orderBy('createdAt', 'desc') + .limit(5) + .with(['comments.author']) + .find(), + + // Top comments by user + Comment.query() + .where('authorId', userId) + .orderBy('likeCount', 'desc') + .limit(10) + .with(['post.author']) + .find(), + + // Activity from people user follows + Post.query() + .whereHas('author', (query) => { + query.whereHas('followers', (followQuery) => { + followQuery.where('followerId', userId); + }); + }) + .orderBy('createdAt', 'desc') + .limit(20) + .with(['author', 'comments']) + .find(), + ]); + + return { + userStats, + recentPosts, + topComments, + followingActivity, + }; +} +``` + +The QueryBuilder provides a powerful, type-safe way to construct complex database queries with support for relationships, caching, pagination, and advanced filtering options. diff --git a/docs/docs/contributing/code-guidelines.md b/docs/docs/contributing/code-guidelines.md new file mode 100644 index 0000000..e457f10 --- /dev/null +++ b/docs/docs/contributing/code-guidelines.md @@ -0,0 +1,734 @@ +--- +sidebar_position: 3 +--- + +# Code Guidelines + +This document outlines the coding standards, best practices, and architectural principles for contributing to DebrosFramework. + +## ๐ŸŽฏ Core Principles + +### 1. Developer Experience First + +Every API decision should prioritize developer experience: + +- **Intuitive naming** - Use clear, descriptive names +- **Consistent patterns** - Follow established conventions +- **Helpful errors** - Provide actionable error messages +- **Complete TypeScript** - Full type safety and IntelliSense + +### 2. Performance by Default + +Optimize for common use cases: + +- **Lazy loading** - Load data only when needed +- **Automatic caching** - Cache frequently accessed data +- **Efficient queries** - Optimize database operations +- **Memory management** - Clean up resources properly + +### 3. Scalability Built-in + +Design for applications with millions of users: + +- **Automatic sharding** - Distribute data effectively +- **Parallel processing** - Execute operations concurrently +- **Resource optimization** - Use resources efficiently +- **Graceful degradation** - Handle failures elegantly + +## ๐Ÿ“ TypeScript Standards + +### Type Safety Requirements + +All code must be fully typed with strict TypeScript: + +```typescript +// โœ… Good - Explicit types +interface UserCreateData { + username: string; + email: string; + bio?: string; +} + +async function createUser(data: UserCreateData): Promise { + // Implementation +} + +// โŒ Bad - Using any +async function createUser(data: any): Promise { + // Implementation +} +``` + +### Interface Design + +Use interfaces for all public APIs: + +```typescript +// โœ… Good - Clear interface definition +interface QueryOptions { + limit?: number; + offset?: number; + orderBy?: OrderByClause[]; + with?: string[]; + cache?: boolean | number; +} + +// โœ… Good - Generic interfaces +interface PaginatedResult { + data: T[]; + total: number; + page: number; + perPage: number; + hasMore: boolean; +} +``` + +### Error Handling + +Use typed error classes with helpful messages: + +```typescript +// โœ… Good - Specific error types +export class ValidationError extends DebrosFrameworkError { + constructor( + public field: string, + public value: any, + public constraint: string, + message?: string, + ) { + super(message || `Validation failed for field '${field}': ${constraint}`, 'VALIDATION_ERROR'); + } +} + +// โœ… Good - Error usage +throw new ValidationError('email', 'invalid-email', 'must be valid email format'); +``` + +### Generic Programming + +Use generics for reusable components: + +```typescript +// โœ… Good - Generic model operations +abstract class BaseModel { + static async create( + this: ModelConstructor, + data: Partial, + ): Promise { + // Implementation + } + + static query(this: ModelConstructor): QueryBuilder { + // Implementation + } +} +``` + +## ๐Ÿ—๏ธ Architecture Patterns + +### Decorator Pattern + +Use decorators consistently for metadata: + +```typescript +// โœ… Good - Consistent decorator usage +@Model({ + scope: 'global', + type: 'docstore', + sharding: { strategy: 'hash', count: 4, key: 'id' }, +}) +export class User extends BaseModel { + @Field({ + type: 'string', + required: true, + unique: true, + validate: (value: string) => value.length >= 3, + }) + username: string; + + @HasMany(() => Post, 'userId') + posts: Post[]; +} +``` + +### Service Pattern + +Use dependency injection for services: + +```typescript +// โœ… Good - Service injection +export class DatabaseManager { + constructor( + private orbitDBService: FrameworkOrbitDBService, + private shardManager: ShardManager, + private configManager: ConfigManager, + ) {} + + async getDatabaseForModel( + modelClass: ModelConstructor, + userId?: string, + ): Promise { + // Implementation + } +} +``` + +### Builder Pattern + +Use builders for complex configuration: + +```typescript +// โœ… Good - Fluent builder API +const migration = createMigration('add_user_profiles', '1.1.0') + .addField('User', 'profilePicture', { type: 'string', required: false }) + .addField('User', 'bio', { type: 'string', required: false }) + .transformData('User', (user) => ({ + ...user, + displayName: user.username || 'Anonymous', + })) + .addValidator('check_profile_data', async (context) => { + // Validation logic + }) + .build(); +``` + +## ๐Ÿ”„ Async/Await Patterns + +### Promise Handling + +Always use async/await instead of Promise chains: + +```typescript +// โœ… Good - async/await +async function getUserWithPosts(userId: string): Promise { + const user = await User.findById(userId); + if (!user) { + throw new NotFoundError('User', userId); + } + + const posts = await Post.query().where('userId', userId).orderBy('createdAt', 'desc').find(); + + user.posts = posts; + return user; +} + +// โŒ Bad - Promise chains +function getUserWithPosts(userId: string): Promise { + return User.findById(userId).then((user) => { + if (!user) { + throw new NotFoundError('User', userId); + } + return Post.query() + .where('userId', userId) + .orderBy('createdAt', 'desc') + .find() + .then((posts) => { + user.posts = posts; + return user; + }); + }); +} +``` + +### Error Propagation + +Let errors bubble up with proper context: + +```typescript +// โœ… Good - Error context +async function createUserWithProfile(userData: UserCreateData): Promise { + try { + const user = await User.create(userData); + + try { + await UserProfile.create({ + userId: user.id, + displayName: userData.username, + }); + } catch (error) { + // Clean up user if profile creation fails + await user.delete(); + throw new OperationError('Failed to create user profile', 'USER_PROFILE_CREATION_FAILED', { + userId: user.id, + originalError: error, + }); + } + + return user; + } catch (error) { + if (error instanceof ValidationError) { + throw error; // Re-throw validation errors as-is + } + throw new OperationError('Failed to create user', 'USER_CREATION_FAILED', { + userData, + originalError: error, + }); + } +} +``` + +## ๐Ÿงช Testing Standards + +### Unit Test Structure + +Use consistent test structure: + +```typescript +// โœ… Good - Clear test structure +describe('User Model', () => { + beforeEach(async () => { + await setupTestDatabase(); + }); + + afterEach(async () => { + await cleanupTestDatabase(); + }); + + describe('create', () => { + it('should create user with valid data', async () => { + // Arrange + const userData = { + username: 'testuser', + email: 'test@example.com', + }; + + // Act + const user = await User.create(userData); + + // Assert + expect(user).toBeDefined(); + expect(user.username).toBe(userData.username); + expect(user.email).toBe(userData.email); + expect(user.id).toBeDefined(); + }); + + it('should throw ValidationError for invalid email', async () => { + // Arrange + const userData = { + username: 'testuser', + email: 'invalid-email', + }; + + // Act & Assert + await expect(User.create(userData)).rejects.toThrow(ValidationError); + }); + }); + + describe('relationships', () => { + it('should load posts relationship', async () => { + // Test relationship loading + }); + }); +}); +``` + +### Integration Test Patterns + +Test real-world scenarios: + +```typescript +// โœ… Good - Integration test +describe('Blog Scenario Integration', () => { + let framework: DebrosFramework; + + beforeAll(async () => { + framework = await setupFrameworkForTesting(); + }); + + afterAll(async () => { + await framework.stop(); + }); + + it('should handle complete blog workflow', async () => { + // Create user + const user = await User.create({ + username: 'blogger', + email: 'blogger@example.com', + }); + + // Create post + const post = await Post.create({ + title: 'Test Post', + content: 'Test content', + userId: user.id, + }); + + // Add comment + const comment = await Comment.create({ + content: 'Great post!', + postId: post.id, + authorId: user.id, + }); + + // Verify relationships + const postWithComments = await Post.query() + .where('id', post.id) + .with(['comments.author']) + .findOne(); + + expect(postWithComments).toBeDefined(); + expect(postWithComments!.comments).toHaveLength(1); + expect(postWithComments!.comments[0].author.username).toBe('blogger'); + }); +}); +``` + +## ๐Ÿ“Š Performance Guidelines + +### Query Optimization + +Write efficient queries: + +```typescript +// โœ… Good - Optimized query +const recentPosts = await Post.query() + .where('publishedAt', '>', Date.now() - 7 * 24 * 60 * 60 * 1000) + .where('isPublished', true) + .with(['author']) // Eager load to avoid N+1 + .orderBy('publishedAt', 'desc') + .limit(20) + .cache(300) // Cache for 5 minutes + .find(); + +// โŒ Bad - Inefficient query +const allPosts = await Post.query().find(); // Loads everything +const recentPosts = allPosts + .filter((p) => p.publishedAt > Date.now() - 7 * 24 * 60 * 60 * 1000) + .filter((p) => p.isPublished) + .slice(0, 20); +``` + +### Memory Management + +Clean up resources properly: + +```typescript +// โœ… Good - Resource cleanup +export class QueryCache { + private cache = new Map(); + private cleanupInterval: NodeJS.Timeout; + + constructor(private ttl: number = 300000) { + this.cleanupInterval = setInterval(() => { + this.cleanup(); + }, this.ttl / 2); + } + + async stop(): Promise { + if (this.cleanupInterval) { + clearInterval(this.cleanupInterval); + } + this.cache.clear(); + } + + private cleanup(): void { + const now = Date.now(); + for (const [key, entry] of this.cache.entries()) { + if (entry.expiresAt < now) { + this.cache.delete(key); + } + } + } +} +``` + +### Async Performance + +Use Promise.all for parallel operations: + +```typescript +// โœ… Good - Parallel execution +async function getUserDashboardData(userId: string): Promise { + const [user, recentPosts, stats, notifications] = await Promise.all([ + User.findById(userId), + Post.query().where('userId', userId).limit(5).find(), + getUserStats(userId), + getRecentNotifications(userId), + ]); + + return { + user, + recentPosts, + stats, + notifications, + }; +} + +// โŒ Bad - Sequential execution +async function getUserDashboardData(userId: string): Promise { + const user = await User.findById(userId); + const recentPosts = await Post.query().where('userId', userId).limit(5).find(); + const stats = await getUserStats(userId); + const notifications = await getRecentNotifications(userId); + + return { + user, + recentPosts, + stats, + notifications, + }; +} +``` + +## ๐Ÿ”ง Code Organization + +### File Structure + +Organize code logically: + +``` +src/framework/ +โ”œโ”€โ”€ core/ # Core framework components +โ”‚ โ”œโ”€โ”€ ConfigManager.ts +โ”‚ โ”œโ”€โ”€ DatabaseManager.ts +โ”‚ โ””โ”€โ”€ ModelRegistry.ts +โ”œโ”€โ”€ models/ # Model system +โ”‚ โ”œโ”€โ”€ BaseModel.ts +โ”‚ โ””โ”€โ”€ decorators/ +โ”‚ โ”œโ”€โ”€ Field.ts +โ”‚ โ”œโ”€โ”€ Model.ts +โ”‚ โ”œโ”€โ”€ relationships.ts +โ”‚ โ””โ”€โ”€ hooks.ts +โ”œโ”€โ”€ query/ # Query system +โ”‚ โ”œโ”€โ”€ QueryBuilder.ts +โ”‚ โ”œโ”€โ”€ QueryExecutor.ts +โ”‚ โ””โ”€โ”€ QueryOptimizer.ts +โ””โ”€โ”€ types/ # Type definitions + โ”œโ”€โ”€ framework.ts + โ”œโ”€โ”€ models.ts + โ””โ”€โ”€ queries.ts +``` + +### Import Organization + +Use consistent import patterns: + +```typescript +// โœ… Good - Organized imports +// Node.js built-ins +import { EventEmitter } from 'events'; +import { promisify } from 'util'; + +// External packages +import { Database } from '@orbitdb/core'; +import { CID } from 'multiformats'; + +// Framework internals +import { BaseModel } from '../models/BaseModel'; +import { ConfigManager } from '../core/ConfigManager'; +import { DatabaseManager } from '../core/DatabaseManager'; + +// Types +import type { ModelConfig, FieldConfig } from '../types/models'; +import type { QueryOptions, QueryResult } from '../types/queries'; +``` + +### Export Patterns + +Use consistent export patterns: + +```typescript +// โœ… Good - Clear exports +// Main class export +export class QueryBuilder { + // Implementation +} + +// Type exports +export type { QueryOptions, QueryResult, WhereClause, OrderByClause }; + +// Utility exports +export { buildWhereClause, optimizeQuery, validateQueryOptions }; + +// Default export for main functionality +export default QueryBuilder; +``` + +## ๐Ÿ“š Documentation Standards + +### JSDoc Comments + +Document all public APIs: + +````typescript +/** + * Creates a new model instance with the provided data. + * + * @template T - The model type extending BaseModel + * @param data - The data to create the model with + * @param options - Optional creation options + * @returns Promise resolving to the created model instance + * + * @throws {ValidationError} When data validation fails + * @throws {DatabaseError} When database operation fails + * + * @example + * ```typescript + * const user = await User.create({ + * username: 'john', + * email: 'john@example.com' + * }); + * ``` + */ +static async create( + this: ModelConstructor, + data: Partial, + options?: CreateOptions +): Promise { + // Implementation +} +```` + +### Code Comments + +Add comments for complex logic: + +```typescript +// โœ… Good - Explaining complex logic +private calculateShardIndex(key: string, shardCount: number): number { + // Use consistent hashing to distribute data evenly across shards + // This ensures that the same key always maps to the same shard + const hash = this.hashFunction(key); + + // Use modulo to map hash to shard index + // Add 1 to avoid negative numbers with certain hash functions + return Math.abs(hash) % shardCount; +} + +// โœ… Good - Explaining business logic +async ensureUserDatabaseExists(userId: string): Promise { + // Check if user database already exists in cache + const existingDb = this.userDatabases.get(userId); + if (existingDb) { + return existingDb; + } + + // Create new user database with user-specific configuration + // This provides data isolation and improved performance + const database = await this.createUserDatabase(userId); + + // Cache the database for future use + this.userDatabases.set(userId, database); + + return database; +} +``` + +## โšก Performance Monitoring + +### Metrics Collection + +Add metrics to important operations: + +```typescript +// โœ… Good - Performance monitoring +export class QueryExecutor { + private metrics = new Map(); + + async executeQuery(query: QueryBuilder): Promise> { + const startTime = Date.now(); + const queryKey = query.toString(); + + try { + const result = await this.internalExecuteQuery(query); + + // Record successful execution + this.recordMetric(queryKey, Date.now() - startTime, true); + + return result; + } catch (error) { + // Record failed execution + this.recordMetric(queryKey, Date.now() - startTime, false); + throw error; + } + } + + private recordMetric(queryKey: string, duration: number, success: boolean): void { + const existing = this.metrics.get(queryKey) || { + count: 0, + totalDuration: 0, + successCount: 0, + averageDuration: 0, + }; + + existing.count++; + existing.totalDuration += duration; + if (success) existing.successCount++; + existing.averageDuration = existing.totalDuration / existing.count; + + this.metrics.set(queryKey, existing); + } +} +``` + +## ๐Ÿ”’ Security Considerations + +### Input Validation + +Validate all inputs thoroughly: + +```typescript +// โœ… Good - Input validation +export class UserService { + async createUser(userData: UserCreateData): Promise { + // Validate required fields + if (!userData.username || typeof userData.username !== 'string') { + throw new ValidationError('username', userData.username, 'required string'); + } + + // Sanitize username + const sanitizedUsername = userData.username.trim().toLowerCase(); + + // Check length constraints + if (sanitizedUsername.length < 3 || sanitizedUsername.length > 20) { + throw new ValidationError('username', sanitizedUsername, 'length between 3-20'); + } + + // Check for valid characters + if (!/^[a-zA-Z0-9_]+$/.test(sanitizedUsername)) { + throw new ValidationError('username', sanitizedUsername, 'alphanumeric and underscore only'); + } + + // Check uniqueness + const existingUser = await User.findOne({ username: sanitizedUsername }); + if (existingUser) { + throw new ConflictError('Username already exists'); + } + + return User.create({ + ...userData, + username: sanitizedUsername, + }); + } +} +``` + +### Error Information + +Don't leak sensitive information in errors: + +```typescript +// โœ… Good - Safe error messages +catch (error) { + if (error instanceof DatabaseConnectionError) { + // Don't expose internal connection details + throw new OperationError( + 'Database operation failed', + 'DATABASE_ERROR', + { operation: 'create_user' } // Safe context only + ); + } + throw error; +} + +// โŒ Bad - Leaking sensitive info +catch (error) { + throw new Error(`Database connection failed: ${error.message} at ${error.stack}`); +} +``` + +--- + +These guidelines help ensure that DebrosFramework maintains high code quality, performance, and developer experience. When in doubt, prioritize clarity, type safety, and developer experience. + +**Next:** Check out our [Testing Guide](./testing-guide) to learn about writing comprehensive tests. diff --git a/docs/docs/contributing/development-setup.md b/docs/docs/contributing/development-setup.md new file mode 100644 index 0000000..e58aa00 --- /dev/null +++ b/docs/docs/contributing/development-setup.md @@ -0,0 +1,475 @@ +--- +sidebar_position: 2 +--- + +# Development Setup + +This guide will help you set up your development environment for contributing to DebrosFramework. + +## ๐Ÿ”ง Prerequisites + +### Required Software + +| Software | Version | Purpose | +| -------------- | ------- | --------------------------- | +| **Node.js** | 18.0+ | Runtime environment | +| **pnpm** | Latest | Package manager (preferred) | +| **Git** | Latest | Version control | +| **Docker** | Latest | Integration testing | +| **TypeScript** | 5.0+ | Development language | + +### Optional Tools + +- **VS Code** - Recommended editor with excellent TypeScript support +- **Docker Desktop** - GUI for Docker management +- **Gitea CLI** (if available) - Command-line interface for our Gitea instance + +## ๐Ÿš€ Environment Setup + +### 1. Repository Access + +#### Create Gitea Account + +1. Visit https://git.debros.io +2. Click "Sign Up" to create an account +3. Verify your email address +4. Request access to the DeBros organization (contact maintainers) + +#### Fork and Clone + +```bash +# Fork the repository through Gitea web interface +# Then clone your fork +git clone https://git.debros.io/DeBros/network.git +cd network + +# Add upstream remote +git remote add upstream https://git.debros.io/DeBros/network.git + +# Verify remotes +git remote -v +# origin https://git.debros.io/DeBros/network.git (fetch) +# origin https://git.debros.io/DeBros/network.git (push) +# upstream https://git.debros.io/DeBros/network.git (fetch) +# upstream https://git.debros.io/DeBros/network.git (push) +``` + +### 2. Project Setup + +#### Install Dependencies + +```bash +# Install pnpm if not already installed +npm install -g pnpm + +# Install project dependencies +pnpm install + +# Install global development tools +pnpm install -g tsx ts-node +``` + +#### Verify Installation + +```bash +# Check versions +node --version # Should be 18.0+ +pnpm --version # Should be latest +tsc --version # Should be 5.0+ + +# Check project setup +pnpm run build # Should complete without errors +``` + +### 3. IDE Configuration + +#### VS Code Setup + +Install recommended extensions: + +```bash +# Install VS Code extensions +code --install-extension ms-vscode.vscode-typescript-next +code --install-extension esbenp.prettier-vscode +code --install-extension ms-vscode.vscode-eslint +code --install-extension bradlc.vscode-tailwindcss +code --install-extension ms-vscode.vscode-json +``` + +Create `.vscode/settings.json`: + +```json +{ + "typescript.preferences.includePackageJsonAutoImports": "auto", + "typescript.suggest.autoImports": true, + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true + }, + "typescript.preferences.importModuleSpecifier": "relative", + "files.exclude": { + "**/node_modules": true, + "**/dist": true, + "**/.git": true + } +} +``` + +#### TypeScript Configuration + +The project includes proper TypeScript configuration in `tsconfig.json`: + +```json +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "node", + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "declaration": true, + "outDir": "./dist", + "rootDir": "./src" + } +} +``` + +## ๐Ÿงช Development Workflow + +### Running the Framework + +#### Basic Build and Test + +```bash +# Clean build +pnpm run clean +pnpm run build + +# Run unit tests +pnpm run test:unit + +# Run integration tests (requires Docker) +pnpm run test:real + +# Run specific blog integration test +pnpm run test:blog-integration +``` + +#### Development Commands + +```bash +# Watch mode for development +pnpm run dev + +# Linting and formatting +pnpm run lint # Check for lint errors +pnpm run lint:fix # Fix auto-fixable lint errors +pnpm run format # Format code with Prettier + +# Clean up +pnpm run clean # Remove build artifacts +``` + +### Testing Setup + +#### Unit Tests + +Unit tests use Jest and are located in `tests/unit/`: + +```bash +# Run all unit tests +pnpm run test:unit + +# Run specific test file +npx jest tests/unit/framework/core/ConfigManager.test.ts + +# Run tests in watch mode +npx jest --watch tests/unit/ +``` + +#### Integration Tests + +Integration tests use Docker to create real IPFS/OrbitDB environments: + +```bash +# Ensure Docker is running +docker --version + +# Run full integration test suite +pnpm run test:real + +# This will: +# 1. Build Docker containers +# 2. Start IPFS bootstrap node +# 3. Start multiple framework instances +# 4. Run real-world scenarios +# 5. Tear down containers +``` + +#### Test Structure + +``` +tests/ +โ”œโ”€โ”€ unit/ # Unit tests +โ”‚ โ”œโ”€โ”€ framework/ # Framework component tests +โ”‚ โ””โ”€โ”€ shared/ # Shared test utilities +โ””โ”€โ”€ real-integration/ # Integration tests + โ””โ”€โ”€ blog-scenario/ # Blog application test scenario + โ”œโ”€โ”€ docker/ # Docker configuration + โ”œโ”€โ”€ scenarios/ # Test scenarios + โ””โ”€โ”€ models/ # Test models +``` + +### Docker Development + +#### Docker Setup for Testing + +The integration tests require Docker. Make sure you have: + +1. **Docker Desktop** installed and running +2. **Docker Compose** available (included with Docker Desktop) +3. **Sufficient memory** allocated to Docker (4GB+ recommended) + +#### Docker Commands + +```bash +# Build test containers +docker-compose -f tests/real-integration/blog-scenario/docker/docker-compose.blog.yml build + +# Run integration tests +pnpm run test:real + +# Clean up Docker resources +docker system prune -f +``` + +## ๐Ÿ”„ Git Workflow + +### Branch Strategy + +We use a feature branch workflow: + +```bash +# Start from main branch +git checkout main +git pull upstream main + +# Create feature branch +git checkout -b feature/your-feature-name + +# Make changes, commit, and push +git add . +git commit -m "feat: add new feature" +git push origin feature/your-feature-name +``` + +### Pull Request Process + +1. **Create Pull Request** in Gitea web interface +2. **Fill out template** with description and testing notes +3. **Request review** from maintainers +4. **Address feedback** if any +5. **Merge** once approved + +## ๐Ÿ› ๏ธ Development Tools + +### Code Quality Tools + +#### ESLint Configuration + +The project uses ESLint for code quality: + +```bash +# Check for issues +pnpm run lint + +# Fix auto-fixable issues +pnpm run lint:fix +``` + +#### Prettier Configuration + +Prettier handles code formatting: + +```bash +# Format all files +pnpm run format + +# Format specific files +npx prettier --write "src/**/*.ts" +``` + +#### Husky Git Hooks + +The project includes pre-commit hooks: + +- **Pre-commit**: Runs lint-staged to format staged files +- **Pre-push**: Runs basic tests to prevent broken code + +### Package Scripts Reference + +```json +{ + "scripts": { + "build": "tsc && tsc-esm-fix --outDir=./dist/esm", + "dev": "tsc -w", + "clean": "rimraf dist", + "lint": "npx eslint src", + "format": "prettier --write \"**/*.{ts,js,json,md}\"", + "lint:fix": "npx eslint src --fix", + "test:unit": "jest tests/unit", + "test:blog-integration": "tsx tests/real-integration/blog-scenario/scenarios/BlogTestRunner.ts", + "test:real": "docker-compose -f tests/real-integration/blog-scenario/docker/docker-compose.blog.yml up --build --abort-on-container-exit", + "prepublishOnly": "npm run clean && npm run build" + } +} +``` + +## ๐Ÿ” Debugging + +### Framework Debugging + +#### Enable Debug Logging + +```typescript +// In your test or development code +const framework = new DebrosFramework({ + monitoring: { + logLevel: 'debug', + enableMetrics: true, + }, +}); +``` + +#### VS Code Debugging + +Create `.vscode/launch.json`: + +```json +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug Unit Tests", + "type": "node", + "request": "launch", + "program": "${workspaceFolder}/node_modules/.bin/jest", + "args": ["--runInBand", "${file}"], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "env": { + "NODE_ENV": "test" + } + }, + { + "name": "Debug Integration Test", + "type": "node", + "request": "launch", + "program": "${workspaceFolder}/node_modules/.bin/tsx", + "args": ["tests/real-integration/blog-scenario/scenarios/BlogTestRunner.ts"], + "console": "integratedTerminal", + "env": { + "NODE_ENV": "test" + } + } + ] +} +``` + +### Common Issues + +#### Node.js Version Issues + +```bash +# Check Node.js version +node --version + +# If using nvm, switch to correct version +nvm use 18 +nvm install 18.19.0 +nvm alias default 18.19.0 +``` + +#### Docker Issues + +```bash +# Check Docker status +docker --version +docker-compose --version + +# Clean Docker cache if tests fail +docker system prune -f +docker volume prune -f +``` + +#### TypeScript Issues + +```bash +# Clear TypeScript cache +npx tsc --build --clean + +# Rebuild project +pnpm run clean +pnpm run build +``` + +## ๐Ÿ“š Additional Resources + +### Learning Resources + +- **[IPFS Documentation](https://docs.ipfs.io/)** - Understanding IPFS concepts +- **[OrbitDB Guide](https://orbitdb.org/getting-started/)** - OrbitDB basics +- **[libp2p Concepts](https://docs.libp2p.io/concepts/)** - Peer-to-peer networking +- **[TypeScript Handbook](https://www.typescriptlang.org/docs/)** - TypeScript reference + +### Community Resources + +- **Gitea Repository**: https://git.debros.io/DeBros/network +- **Documentation**: This documentation site +- **Discord** (if available): Community chat +- **Email**: Contact maintainers for access and questions + +--- + +## โœ… Setup Verification + +Run this checklist to verify your setup: + +```bash +# 1. Check Node.js version +node --version # Should be 18.0+ + +# 2. Check package manager +pnpm --version # Should be latest + +# 3. Install dependencies +pnpm install # Should complete without errors + +# 4. Build project +pnpm run build # Should complete without errors + +# 5. Run linting +pnpm run lint # Should pass with no errors + +# 6. Run unit tests +pnpm run test:unit # Should pass all tests + +# 7. Check Docker (optional, for integration tests) +docker --version # Should show Docker version +pnpm run test:real # Should run integration tests +``` + +If all steps pass, you're ready to contribute! ๐ŸŽ‰ + +--- + +**Next Steps:** + +- Read our **[Code Guidelines](./code-guidelines)** to understand coding standards +- Check out **[Testing Guide](./testing-guide)** to learn about writing tests +- Browse existing issues in Gitea to find something to work on diff --git a/docs/docs/contributing/overview.md b/docs/docs/contributing/overview.md new file mode 100644 index 0000000..8441cb4 --- /dev/null +++ b/docs/docs/contributing/overview.md @@ -0,0 +1,214 @@ +--- +sidebar_position: 1 +--- + +# Contributing to DebrosFramework + +Welcome to the DebrosFramework contributor community! We're excited to have you help build the future of decentralized application development. + +## ๐ŸŒŸ Why Contribute? + +DebrosFramework is an ambitious project that aims to make decentralized application development as simple as traditional web development. Your contributions help: + +- **Advance decentralized technology** - Make dApps more accessible to developers +- **Build better tools** - Create powerful abstractions over IPFS and OrbitDB +- **Shape the future** - Influence how decentralized applications are built +- **Learn cutting-edge tech** - Work with the latest in distributed systems +- **Join a community** - Connect with like-minded developers + +## ๐Ÿš€ Development Status + +**Current Version**: 0.5.0-beta +**Status**: Active Development - Beta Release + +DebrosFramework is in active beta development. The core architecture is not stable, but we're continuously improving APIs, adding features, and optimizing performance. This is an excellent time to contribute as your input can significantly shape the framework's direction. + +### What's Ready for Contribution + +โœ… **Core Framework** - Stable architecture, ready for enhancements +โœ… **Model System** - Decorator-based models with validation +โœ… **Query Builder** - Rich querying with optimization +โœ… **Relationship System** - Complex data relationships +โœ… **Sharding** - Automatic data distribution +โœ… **Migration System** - Schema evolution tools +โœ… **Documentation** - Comprehensive guides and examples + +### What's Coming Next + +๐Ÿ”„ **Performance Optimization** - Query caching and execution improvements +๐Ÿ”„ **Advanced Features** - Real-time subscriptions and event systems +๐Ÿ”„ **Developer Experience** - Better tooling and debugging +๐Ÿ”„ **Production Ready** - Stability and performance for production use + +## ๐Ÿ—๏ธ Repository Information + +### Self-Hosted Git Repository + +We use a **self-hosted Gitea instance** instead of GitHub: + +**Repository URL**: https://git.debros.io/DeBros/network + +**Why Gitea?** + +- **Decentralization aligned** - Fits our philosophy of decentralized systems +- **Full control** - Complete control over our development infrastructure +- **Privacy focused** - No external dependencies for sensitive development data +- **Community owned** - Aligns with our open-source, community-driven approach + +### Getting Repository Access + +1. **Create an account** at https://git.debros.io +2. **Request access** by contacting the maintainers +3. **Fork the repository** to your account +4. **Clone your fork** locally + +```bash +git clone https://git.debros.io/DeBros/network.git +cd network +``` + +## ๐Ÿค How to Contribute + +### Types of Contributions Welcome + +| Type | Description | Skill Level | Time Commitment | +| ----------------------- | ------------------------------ | --------------------- | --------------- | +| ๐Ÿ› **Bug Reports** | Find and report issues | Beginner | Low | +| ๐Ÿ“– **Documentation** | Improve guides and examples | Beginner-Intermediate | Low-Medium | +| โœจ **Feature Requests** | Suggest new capabilities | Any | Low | +| ๐Ÿงช **Testing** | Write tests and test scenarios | Intermediate | Medium | +| ๐Ÿ”ง **Bug Fixes** | Fix reported issues | Intermediate | Medium | +| โšก **Performance** | Optimize existing code | Advanced | Medium-High | +| ๐Ÿš€ **New Features** | Implement new functionality | Advanced | High | +| ๐Ÿ—๏ธ **Architecture** | Design system improvements | Expert | High | + +### Contribution Areas + +#### ๐ŸŽฏ High Priority Areas + +1. **Integration Tests** - Real-world scenario testing +2. **Performance Optimization** - Query execution and caching +3. **Developer Experience** - Better error messages and debugging +4. **Documentation** - More examples and use cases +5. **Type Safety** - Improved TypeScript definitions + +#### ๐Ÿ”ฅ Hot Topics + +- **Real-time Features** - PubSub improvements and real-time data sync +- **Migration Tools** - Better schema evolution and data transformation +- **Query Optimization** - Smarter query planning and execution +- **Monitoring** - Performance metrics and health checking +- **CLI Tools** - Development and deployment tooling + +## ๐Ÿ› ๏ธ Technical Overview + +### Architecture Understanding + +Before contributing code, familiarize yourself with the framework architecture: + +``` +DebrosFramework/ +โ”œโ”€โ”€ Core Layer (ConfigManager, DatabaseManager) +โ”œโ”€โ”€ Model Layer (BaseModel, Decorators, Validation) +โ”œโ”€โ”€ Query Layer (QueryBuilder, QueryExecutor, Optimization) +โ”œโ”€โ”€ Relationship Layer (RelationshipManager, LazyLoader) +โ”œโ”€โ”€ Sharding Layer (ShardManager, Distribution) +โ”œโ”€โ”€ Migration Layer (MigrationManager, MigrationBuilder) +โ”œโ”€โ”€ Feature Layer (PinningManager, PubSubManager) +โ””โ”€โ”€ Service Layer (OrbitDBService, IPFSService) +``` + +### Key Technologies + +- **TypeScript** - Primary development language +- **OrbitDB** - Distributed database layer +- **IPFS/Helia** - Distributed storage layer +- **libp2p** - Peer-to-peer networking +- **Docker** - Containerization for testing +- **Jest** - Unit testing framework +- **Prettier/ESLint** - Code formatting and linting + +### Development Philosophy + +1. **Developer Experience First** - APIs should be intuitive and well-documented +2. **Type Safety** - Comprehensive TypeScript support throughout +3. **Performance by Default** - Optimize common use cases automatically +4. **Flexibility** - Support diverse application patterns +5. **Reliability** - Robust error handling and recovery +6. **Scalability** - Design for applications with millions of users + +## ๐Ÿ“‹ Getting Started Checklist + +Before making your first contribution: + +- [ ] Read this contributor guide completely +- [ ] Set up your development environment +- [ ] Run the test suite successfully +- [ ] Explore the codebase and documentation +- [ ] Join our community channels +- [ ] Choose your first contribution area +- [ ] Check existing issues and discussions + +## ๐Ÿ”— Quick Links + +- **[Development Setup](./development-setup)** - Get your environment ready +- **[Code Guidelines](./code-guidelines)** - Coding standards and best practices +- **[Testing Guide](./testing-guide)** - How to write and run tests +- **[Documentation Guide](./documentation-guide)** - Contributing to docs +- **[Release Process](./release-process)** - How we ship new versions +- **[Community](./community)** - Connect with other contributors + +## ๐Ÿ’ก First Time Contributors + +New to open source or DebrosFramework? Start here: + +1. **Good First Issues** - Look for issues tagged `good-first-issue` in our Gitea repository +2. **Documentation** - Help improve our guides and examples +3. **Testing** - Add test cases for existing functionality +4. **Examples** - Create new usage examples and tutorials + +## ๐ŸŽฏ Contributor Levels + +### ๐ŸŒฑ **Beginner Contributors** + +- Report bugs and suggest improvements +- Fix typos and improve documentation +- Add simple test cases +- Create usage examples + +### ๐ŸŒฟ **Intermediate Contributors** + +- Fix bugs and implement small features +- Improve existing functionality +- Write comprehensive tests +- Contribute to API design discussions + +### ๐ŸŒณ **Advanced Contributors** + +- Implement major features +- Optimize performance-critical code +- Design architectural improvements +- Mentor other contributors + +### ๐Ÿ† **Core Contributors** + +- Drive technical direction +- Review and merge contributions +- Manage releases and roadmap +- Represent the project publicly + +## ๐Ÿ… Recognition + +Contributors are recognized through: + +- **Contributor list** in repository and documentation +- **Release notes** crediting significant contributions +- **Community highlights** in announcements +- **Direct contributor access** for consistent contributors +- **Maintainer status** for exceptional long-term contributors + +--- + +Ready to contribute? Head over to our **[Development Setup Guide](./development-setup)** to get started! + +_Have questions? Join our community channels or reach out to the maintainers. We're here to help! ๐Ÿš€_ diff --git a/docs/docs/intro.md b/docs/docs/intro.md index bdcee8b..e814570 100644 --- a/docs/docs/intro.md +++ b/docs/docs/intro.md @@ -109,6 +109,18 @@ DebrosFramework is perfect for developers who want to: Ready to build your first decentralized application? Check out our [Getting Started Guide](./getting-started) to set up your development environment and create your first models. +## Development Status + +**Current Version**: 0.5.0-beta +**Status**: Active Development - Beta Release + +DebrosFramework is currently in beta and under active development. The core architecture and APIs are stabilizing, but some features may still change. We recommend using it for: + +- โœ… **Development and Testing**: Perfect for building and testing decentralized applications +- โœ… **Prototyping**: Rapid prototyping of dApp concepts +- โœ… **Learning**: Understanding decentralized application architecture +- โš ๏ธ **Production**: Use with caution - APIs may change in future versions + ## Community and Support - ๐Ÿ“– [Documentation](./getting-started) - Comprehensive guides and examples diff --git a/docs/docs/videos/index.md b/docs/docs/videos/index.md new file mode 100644 index 0000000..e1aa902 --- /dev/null +++ b/docs/docs/videos/index.md @@ -0,0 +1,691 @@ +--- +sidebar_position: 6 +--- + +# Video Tutorials + +Learn Debros Network through comprehensive video tutorials. These step-by-step guides will help you master the framework from basic concepts to advanced implementations. + +## ๐ŸŽฌ **Getting Started Series** + +### 1. Introduction to Debros Network + +**Duration: 15 minutes** + +A complete overview of Debros Network, its architecture, and core concepts. Perfect for developers new to decentralized applications. + +**What You'll Learn:** + +- Framework architecture and components +- Key differences from traditional frameworks +- When to use Debros Network +- Development environment overview + +```typescript +// Code examples from this video +import { DebrosFramework, BaseModel, Model, Field } from '@debros/network'; + +@Model({ + scope: 'global', + type: 'docstore', +}) +class User extends BaseModel { + @Field({ type: 'string', required: true }) + username: string; +} +``` + +[**โ–ถ๏ธ Watch Introduction Video**](https://youtube.com/watch?v=VIDEO_ID_HERE) + +--- + +### 2. Setting Up Your Development Environment + +**Duration: 20 minutes** + +Step-by-step guide to setting up Debros Network in your development environment. + +**What You'll Learn:** + +- Installing dependencies +- Project structure setup +- IDE configuration +- Development tools + +**Prerequisites:** + +- Node.js 18+ +- npm or pnpm +- TypeScript knowledge + +```bash +# Commands from this video +npm create debros-app my-app +cd my-app +npm install +npm run dev +``` + +[**โ–ถ๏ธ Watch Setup Video**](https://youtube.com/watch?v=VIDEO_ID_HERE) + +--- + +### 3. Your First Debros Application + +**Duration: 25 minutes** + +Build your first decentralized application with user management and basic CRUD operations. + +**What You'll Learn:** + +- Creating models with decorators +- Database initialization +- Basic CRUD operations +- Error handling + +**Project Files:** + +- `models/User.ts` +- `models/Post.ts` +- `app.ts` + +```typescript +// Final code from this tutorial +@Model({ + scope: 'global', + type: 'docstore', + sharding: { strategy: 'hash', count: 4, key: 'id' }, +}) +export class User extends BaseModel { + @Field({ type: 'string', required: true, unique: true }) + username: string; + + @Field({ type: 'string', required: true, unique: true }) + email: string; + + @HasMany(() => Post, 'authorId') + posts: Post[]; +} +``` + +[**โ–ถ๏ธ Watch First App Video**](https://youtube.com/watch?v=VIDEO_ID_HERE) + +--- + +## ๐Ÿ—๏ธ **Core Concepts Series** + +### 4. Understanding Models and Decorators + +**Duration: 30 minutes** + +Deep dive into the model system, decorators, and data validation. + +**Topics Covered:** + +- Model configuration options +- Field types and validation +- Custom validators +- Lifecycle hooks +- Best practices + +```typescript +// Advanced model example from video +@Model({ + scope: 'user', + type: 'docstore', + sharding: { strategy: 'user', count: 2, key: 'userId' }, +}) +export class BlogPost extends BaseModel { + @Field({ + type: 'string', + required: true, + minLength: 5, + maxLength: 200, + validate: (title: string) => !title.includes('spam'), + }) + title: string; + + @BeforeCreate() + async validatePost() { + // Custom validation logic + } +} +``` + +[**โ–ถ๏ธ Watch Models Deep Dive**](https://youtube.com/watch?v=VIDEO_ID_HERE) + +--- + +### 5. Mastering the Query System + +**Duration: 35 minutes** + +Complete guide to building complex queries with relationships and optimization. + +**Topics Covered:** + +- Query builder API +- Filtering and sorting +- Relationship loading +- Pagination +- Query optimization +- Caching strategies + +```typescript +// Complex query example from video +const results = await User.query() + .where('isActive', true) + .where('score', '>', 100) + .whereHas('posts', (query) => { + query.where('isPublished', true).where('createdAt', '>', Date.now() - 30 * 24 * 60 * 60 * 1000); + }) + .with(['posts.comments.author', 'profile']) + .orderBy('score', 'desc') + .cache(300) + .paginate(1, 20); +``` + +[**โ–ถ๏ธ Watch Query Mastery**](https://youtube.com/watch?v=VIDEO_ID_HERE) + +--- + +### 6. Working with Relationships + +**Duration: 25 minutes** + +Understanding and implementing relationships between models. + +**Topics Covered:** + +- Relationship types (HasMany, BelongsTo, etc.) +- Eager vs lazy loading +- Nested relationships +- Performance considerations + +```typescript +// Relationship examples from video +@Model({ scope: 'global', type: 'docstore' }) +export class User extends BaseModel { + @HasMany(() => Post, 'authorId') + posts: Post[]; + + @ManyToMany(() => User, 'followers', 'following') + followers: User[]; + + @HasOne(() => UserProfile, 'userId') + profile: UserProfile; +} +``` + +[**โ–ถ๏ธ Watch Relationships Video**](https://youtube.com/watch?v=VIDEO_ID_HERE) + +--- + +## ๐Ÿš€ **Advanced Features Series** + +### 7. Database Sharding and Scaling + +**Duration: 40 minutes** + +Learn how to scale your application with automatic sharding strategies. + +**Topics Covered:** + +- Sharding strategies +- User-scoped vs global databases +- Performance optimization +- Monitoring and metrics + +```typescript +// Sharding configuration examples +@Model({ + scope: 'user', + type: 'docstore', + sharding: { + strategy: 'hash', + count: 8, + key: 'userId', + }, +}) +export class UserData extends BaseModel { + // Model implementation +} +``` + +[**โ–ถ๏ธ Watch Sharding Guide**](https://youtube.com/watch?v=VIDEO_ID_HERE) + +--- + +### 8. Migrations and Schema Evolution + +**Duration: 30 minutes** + +Managing database schema changes and data migrations. + +**Topics Covered:** + +- Creating migrations +- Data transformations +- Rollback strategies +- Production deployment + +```typescript +// Migration example from video +const migration = createMigration('add_user_profiles', '1.1.0') + .addField('User', 'profilePicture', { + type: 'string', + required: false, + }) + .addField('User', 'bio', { + type: 'string', + required: false, + }) + .transformData('User', (user) => ({ + ...user, + displayName: user.username || 'Anonymous', + })) + .build(); +``` + +[**โ–ถ๏ธ Watch Migrations Video**](https://youtube.com/watch?v=VIDEO_ID_HERE) + +--- + +### 9. Real-time Features and PubSub + +**Duration: 25 minutes** + +Implementing real-time functionality with the built-in PubSub system. + +**Topics Covered:** + +- Event publishing +- Real-time subscriptions +- WebSocket integration +- Performance considerations + +```typescript +// Real-time examples from video +@Model({ scope: 'global', type: 'docstore' }) +export class ChatMessage extends BaseModel { + @AfterCreate() + async publishMessage() { + await this.publish('message:created', { + roomId: this.roomId, + message: this, + }); + } +} +``` + +[**โ–ถ๏ธ Watch Real-time Video**](https://youtube.com/watch?v=VIDEO_ID_HERE) + +--- + +## ๐Ÿ› ๏ธ **Project Tutorials** + +### 10. Building a Complete Blog Application + +**Duration: 60 minutes** + +Build a full-featured blog application with authentication, posts, and comments. + +**Features Built:** + +- User authentication +- Post creation and editing +- Comment system +- User profiles +- Admin dashboard + +**Final Project Structure:** + +``` +blog-app/ +โ”œโ”€โ”€ models/ +โ”‚ โ”œโ”€โ”€ User.ts +โ”‚ โ”œโ”€โ”€ Post.ts +โ”‚ โ”œโ”€โ”€ Comment.ts +โ”‚ โ””โ”€โ”€ Category.ts +โ”œโ”€โ”€ services/ +โ”‚ โ”œโ”€โ”€ AuthService.ts +โ”‚ โ””โ”€โ”€ BlogService.ts +โ””โ”€โ”€ app.ts +``` + +[**โ–ถ๏ธ Watch Blog Tutorial**](https://youtube.com/watch?v=VIDEO_ID_HERE) + +--- + +### 11. Creating a Social Media Platform + +**Duration: 90 minutes** + +Build a decentralized social media platform with advanced features. + +**Features Built:** + +- User profiles and following +- Feed generation +- Real-time messaging +- Content moderation +- Analytics dashboard + +**Part 1: User System and Profiles** (30 min) +[**โ–ถ๏ธ Watch Part 1**](https://youtube.com/watch?v=VIDEO_ID_HERE) + +**Part 2: Posts and Feed** (30 min) +[**โ–ถ๏ธ Watch Part 2**](https://youtube.com/watch?v=VIDEO_ID_HERE) + +**Part 3: Real-time Features** (30 min) +[**โ–ถ๏ธ Watch Part 3**](https://youtube.com/watch?v=VIDEO_ID_HERE) + +--- + +### 12. E-commerce Platform with Debros Network + +**Duration: 75 minutes** + +Build a decentralized e-commerce platform with product management and orders. + +**Features Built:** + +- Product catalog +- Shopping cart +- Order processing +- Inventory management +- Payment integration + +**Part 1: Product Management** (25 min) +[**โ–ถ๏ธ Watch Part 1**](https://youtube.com/watch?v=VIDEO_ID_HERE) + +**Part 2: Shopping and Orders** (25 min) +[**โ–ถ๏ธ Watch Part 2**](https://youtube.com/watch?v=VIDEO_ID_HERE) + +**Part 3: Advanced Features** (25 min) +[**โ–ถ๏ธ Watch Part 3**](https://youtube.com/watch?v=VIDEO_ID_HERE) + +--- + +## ๐Ÿ”ง **Development Workflow Series** + +### 13. Testing Strategies for Debros Applications + +**Duration: 35 minutes** + +Comprehensive testing approaches for decentralized applications. + +**Topics Covered:** + +- Unit testing models +- Integration testing +- Mocking strategies +- Performance testing + +```typescript +// Testing example from video +describe('User Model', () => { + it('should create user with valid data', async () => { + const user = await User.create({ + username: 'testuser', + email: 'test@example.com', + }); + + expect(user.id).toBeDefined(); + expect(user.username).toBe('testuser'); + }); +}); +``` + +[**โ–ถ๏ธ Watch Testing Video**](https://youtube.com/watch?v=VIDEO_ID_HERE) + +--- + +### 14. Deployment and Production Best Practices + +**Duration: 45 minutes** + +Deploy your Debros Network applications to production environments. + +**Topics Covered:** + +- Production configuration +- Docker containers +- Monitoring and logging +- Performance optimization +- Security considerations + +```dockerfile +# Docker example from video +FROM node:18-alpine +WORKDIR /app +COPY package*.json ./ +RUN npm ci --only=production +COPY . . +RUN npm run build +EXPOSE 3000 +CMD ["npm", "start"] +``` + +[**โ–ถ๏ธ Watch Deployment Video**](https://youtube.com/watch?v=VIDEO_ID_HERE) + +--- + +### 15. Performance Optimization Techniques + +**Duration: 40 minutes** + +Advanced techniques for optimizing Debros Network applications. + +**Topics Covered:** + +- Query optimization +- Caching strategies +- Database indexing +- Memory management +- Profiling tools + +```typescript +// Optimization examples from video +// Efficient query with proper indexing +const optimizedQuery = await User.query() + .where('isActive', true) // Indexed field + .with(['posts']) // Eager load to avoid N+1 + .cache(300) // Cache frequently accessed data + .limit(50) // Reasonable limits + .find(); +``` + +[**โ–ถ๏ธ Watch Optimization Video**](https://youtube.com/watch?v=VIDEO_ID_HERE) + +--- + +## ๐Ÿ“ฑ **Integration Series** + +### 16. Frontend Integration with React + +**Duration: 50 minutes** + +Integrate Debros Network with React applications for full-stack development. + +**Topics Covered:** + +- React hooks for Debros +- State management +- Real-time updates +- Error boundaries + +```typescript +// React integration example from video +import { useDebrosQuery, useDebrosModel } from '@debros/react'; + +function UserProfile({ userId }: { userId: string }) { + const { user, loading, error } = useDebrosModel(User, userId, { + with: ['posts', 'profile'] + }); + + if (loading) return
Loading...
; + if (error) return
Error: {error.message}
; + + return ( +
+

{user.username}

+

Posts: {user.posts.length}

+
+ ); +} +``` + +[**โ–ถ๏ธ Watch React Integration**](https://youtube.com/watch?v=VIDEO_ID_HERE) + +--- + +### 17. Building APIs with Express and Debros + +**Duration: 35 minutes** + +Create REST APIs using Express.js with Debros Network as the backend. + +**Topics Covered:** + +- Express middleware +- API route design +- Authentication +- Error handling +- API documentation + +```typescript +// Express API example from video +app.get('/api/users/:id', async (req, res) => { + try { + const user = await User.findById(req.params.id, { + with: ['posts'], + }); + + if (!user) { + return res.status(404).json({ error: 'User not found' }); + } + + res.json(user); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}); +``` + +[**โ–ถ๏ธ Watch Express Integration**](https://youtube.com/watch?v=VIDEO_ID_HERE) + +--- + +### 18. Mobile Development with React Native + +**Duration: 45 minutes** + +Build mobile applications using React Native and Debros Network. + +**Topics Covered:** + +- React Native setup +- Offline synchronization +- Mobile-specific optimizations +- Push notifications + +[**โ–ถ๏ธ Watch Mobile Development**](https://youtube.com/watch?v=VIDEO_ID_HERE) + +--- + +## ๐ŸŽ“ **Masterclass Series** + +### 19. Architecture Patterns and Best Practices + +**Duration: 60 minutes** + +Advanced architectural patterns for large-scale Debros Network applications. + +**Topics Covered:** + +- Domain-driven design +- CQRS patterns +- Event sourcing +- Microservices architecture + +[**โ–ถ๏ธ Watch Architecture Masterclass**](https://youtube.com/watch?v=VIDEO_ID_HERE) + +--- + +### 20. Contributing to Debros Network + +**Duration: 30 minutes** + +Learn how to contribute to the Debros Network open-source project. + +**Topics Covered:** + +- Development setup +- Code standards +- Testing requirements +- Pull request process + +[**โ–ถ๏ธ Watch Contributing Guide**](https://youtube.com/watch?v=VIDEO_ID_HERE) + +--- + +## ๐Ÿ“š **Additional Resources** + +### Video Playlists + +**๐ŸŽฌ [Complete Beginner Series](https://youtube.com/playlist?list=PLAYLIST_ID)** +Videos 1-6: Everything you need to get started + +**๐Ÿ—๏ธ [Advanced Development](https://youtube.com/playlist?list=PLAYLIST_ID)** +Videos 7-12: Advanced features and patterns + +**๐Ÿ› ๏ธ [Project Tutorials](https://youtube.com/playlist?list=PLAYLIST_ID)** +Videos 10-12: Complete project walkthroughs + +**๐Ÿ”ง [Production Ready](https://youtube.com/playlist?list=PLAYLIST_ID)** +Videos 13-15: Testing, deployment, and optimization + +### Community Videos + +**Community Showcase** + +- [Building a Decentralized Chat App](https://youtube.com/watch?v=COMMUNITY_VIDEO_1) +- [E-learning Platform with Debros](https://youtube.com/watch?v=COMMUNITY_VIDEO_2) +- [IoT Data Management](https://youtube.com/watch?v=COMMUNITY_VIDEO_3) + +### Interactive Learning + +**๐ŸŽฎ [Interactive Tutorials](https://learn.debros.io)** +Hands-on coding exercises with instant feedback + +**๐Ÿ’ฌ [Discord Community](https://discord.gg/debros)** +Get help and discuss videos with other developers + +**๐Ÿ“– [Workshop Materials](https://github.com/debros/workshops)** +Download code samples and workshop materials + +--- + +## ๐Ÿ“… **Release Schedule** + +New video tutorials are released every **Tuesday and Friday**: + +- **Tuesdays**: Core concepts and feature deep-dives +- **Fridays**: Project tutorials and real-world applications + +**Subscribe to our [YouTube channel](https://youtube.com/@debrosnetwork)** and enable notifications to stay updated with the latest tutorials. + +## ๐Ÿค **Request a Tutorial** + +Have a specific topic you'd like covered? Request a tutorial: + +- **[GitHub Discussions](https://github.com/debros/network/discussions)** - Community requests +- **[Email Us](mailto:tutorials@debros.io)** - Direct requests +- **[Discord](https://discord.gg/debros)** - Community suggestions + +We prioritize tutorials based on community demand and framework updates. + +--- + +_All videos include closed captions, downloadable code samples, and companion blog posts for reference._ diff --git a/docs/docusaurus.config.ts b/docs/docusaurus.config.ts index 801fe9c..b146d76 100644 --- a/docs/docusaurus.config.ts +++ b/docs/docusaurus.config.ts @@ -1,12 +1,12 @@ -import {themes as prismThemes} from 'prism-react-renderer'; -import type {Config} from '@docusaurus/types'; +import { themes as prismThemes } from 'prism-react-renderer'; +import type { Config } from '@docusaurus/types'; import type * as Preset from '@docusaurus/preset-classic'; // This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) const config: Config = { - title: 'DebrosFramework', - tagline: 'Build scalable decentralized applications with ease', + title: 'Debros Network', + tagline: 'Next-Generation Decentralized Web Framework', favicon: 'img/favicon.ico', // Future flags, see https://docusaurus.io/docs/api/docusaurus-config#future @@ -71,13 +71,20 @@ const config: Config = { themeConfig: { // Replace with your project's social card - image: 'img/docusaurus-social-card.jpg', + image: 'img/debros-social-card.jpg', + colorMode: { + defaultMode: 'dark', + disableSwitch: false, + respectPrefersColorScheme: false, + }, navbar: { - title: 'DebrosFramework', - logo: { - alt: 'DebrosFramework Logo', - src: 'img/logo.svg', - }, + title: 'Debros Network', + // logo: { + // alt: 'Debros Network Logo', + // src: 'img/logo.svg', + // srcDark: 'img/logo-dark.svg', + // }, + hideOnScroll: true, items: [ { type: 'docSidebar', @@ -91,10 +98,14 @@ const config: Config = { position: 'left', label: 'API Reference', }, - {to: '/blog', label: 'Blog', position: 'left'}, + { + href: 'https://git.debros.io/DeBros/network', + label: 'Gitea', + position: 'right', + }, { href: 'https://github.com/debros/network', - label: 'GitHub', + label: 'GitHub Mirror', position: 'right', }, ], @@ -103,11 +114,23 @@ const config: Config = { style: 'dark', links: [ { - title: 'Docs', + title: 'Documentation', items: [ { - label: 'Tutorial', - to: '/docs/intro', + label: 'Getting Started', + to: '/docs/getting-started', + }, + { + label: 'Core Concepts', + to: '/docs/core-concepts/architecture', + }, + { + label: 'API Reference', + to: '/docs/api/overview', + }, + { + label: 'Contributing', + to: '/docs/contributing/overview', }, ], }, @@ -115,34 +138,34 @@ const config: Config = { title: 'Community', items: [ { - label: 'Stack Overflow', - href: 'https://stackoverflow.com/questions/tagged/docusaurus', + label: 'Gitea Repository', + href: 'https://git.debros.io/DeBros/network', }, { - label: 'Discord', - href: 'https://discordapp.com/invite/docusaurus', + label: 'GitHub Mirror', + href: 'https://github.com/debros/network', }, { - label: 'X', - href: 'https://x.com/docusaurus', + label: 'Issues & Support', + href: 'https://git.debros.io/DeBros/network/issues', }, ], }, { - title: 'More', + title: 'Resources', items: [ { - label: 'Blog', - to: '/blog', + label: 'Examples', + to: '/docs/examples/basic-usage', }, { - label: 'GitHub', - href: 'https://github.com/facebook/docusaurus', + label: 'License', + href: 'https://git.debros.io/DeBros/network/src/branch/main/LICENSE', }, ], }, ], - copyright: `Copyright ยฉ ${new Date().getFullYear()} DebrosFramework. Built with Docusaurus.`, + copyright: `Copyright ยฉ ${new Date().getFullYear()} Debros Network. Empowering the decentralized web.`, }, prism: { theme: prismThemes.github, diff --git a/docs/sidebars.ts b/docs/sidebars.ts index 4b17fc8..aa484d8 100644 --- a/docs/sidebars.ts +++ b/docs/sidebars.ts @@ -40,6 +40,22 @@ const sidebars: SidebarsConfig = { 'examples/basic-usage', ], }, + { + type: 'category', + label: 'Video Tutorials', + items: [ + 'videos/index', + ], + }, + { + type: 'category', + label: 'Contributing', + items: [ + 'contributing/overview', + 'contributing/development-setup', + 'contributing/code-guidelines', + ], + }, ], // API Reference sidebar @@ -47,9 +63,18 @@ const sidebars: SidebarsConfig = { 'api/overview', { type: 'category', - label: 'Framework Classes', + label: 'Core Classes', items: [ 'api/debros-framework', + 'api/base-model', + 'api/query-builder', + ], + }, + { + type: 'category', + label: 'Network API', + items: [ + 'api/network-api', ], }, ], diff --git a/docs/src/components/HomepageFeatures/index.tsx b/docs/src/components/HomepageFeatures/index.tsx index c2551fb..bb3126d 100644 --- a/docs/src/components/HomepageFeatures/index.tsx +++ b/docs/src/components/HomepageFeatures/index.tsx @@ -5,52 +5,86 @@ import styles from './styles.module.css'; type FeatureItem = { title: string; - Svg: React.ComponentType>; + icon: string; description: ReactNode; + highlight?: boolean; }; const FeatureList: FeatureItem[] = [ { - title: 'Easy to Use', - Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default, + title: 'Decentralized by Design', + icon: '๐ŸŒ', description: ( <> - Docusaurus was designed from the ground up to be easily installed and - used to get your website up and running quickly. + Built on OrbitDB and IPFS, DebrosFramework + creates truly decentralized applications that don't rely on centralized servers + or single points of failure. + + ), + highlight: true, + }, + { + title: 'Developer Experience First', + icon: 'โšก', + description: ( + <> + Full TypeScript support with intelligent auto-completion, + decorator-based models, and intuitive APIs that make building complex + applications feel effortless. ), }, { - title: 'Focus on What Matters', - Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default, + title: 'Infinite Scalability', + icon: '๐Ÿš€', description: ( <> - Docusaurus lets you focus on your docs, and we'll do the chores. Go - ahead and move your docs into the docs directory. + Automatic sharding, efficient queries, and built-in caching ensure your + applications can scale to millions of users without architectural changes. ), }, { - title: 'Powered by React', - Svg: require('@site/static/img/undraw_docusaurus_react.svg').default, + title: 'Zero Configuration', + icon: '๐ŸŽฏ', description: ( <> - Extend or customize your website layout by reusing React. Docusaurus can - be extended while reusing the same header and footer. + Start building immediately with sensible defaults. No complex setup, + no configuration files, no DevOps headachesโ€”just pure development focus. + + ), + }, + { + title: 'Real-time Sync', + icon: '๐Ÿ”„', + description: ( + <> + Built-in real-time synchronization across all peers. Changes propagate + instantly across the network with conflict resolution and offline support. + + ), + }, + { + title: 'Enterprise Ready', + icon: '๐Ÿ”’', + description: ( + <> + Production-grade security, comprehensive testing, detailed documentation, + and enterprise support make DebrosFramework ready for mission-critical applications. ), }, ]; -function Feature({title, Svg, description}: FeatureItem) { +function Feature({title, icon, description, highlight}: FeatureItem) { return ( -
-
- +
+
+ {icon}
-
- {title} -

{description}

+
+ {title} +

{description}

); @@ -60,7 +94,15 @@ export default function HomepageFeatures(): ReactNode { return (
-
+
+ + Why Choose Debros Network? + +

+ Everything you need to build the next generation of decentralized applications +

+
+
{FeatureList.map((props, idx) => ( ))} diff --git a/docs/src/components/HomepageFeatures/styles.module.css b/docs/src/components/HomepageFeatures/styles.module.css index b248eb2..08c0818 100644 --- a/docs/src/components/HomepageFeatures/styles.module.css +++ b/docs/src/components/HomepageFeatures/styles.module.css @@ -1,11 +1,191 @@ +/* Features Section */ .features { - display: flex; - align-items: center; - padding: 2rem 0; - width: 100%; + padding: 6rem 0; + background: var(--ifm-background-color); } -.featureSvg { - height: 200px; - width: 200px; +.featuresHeader { + text-align: center; + margin-bottom: 4rem; +} + +.featuresTitle { + font-size: 2.5rem; + font-weight: 800; + background: var(--debros-gradient-secondary); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + margin-bottom: 1rem; +} + +.featuresSubtitle { + font-size: 1.2rem; + color: var(--ifm-color-content-secondary); + max-width: 600px; + margin: 0 auto; + line-height: 1.6; +} + +.featuresGrid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); + gap: 2rem; + margin-top: 2rem; +} + +.featureCard { + display: flex; + flex-direction: column; + height: 100%; + padding: 2.5rem; + background: var(--ifm-background-surface-color); + border: 1px solid rgba(139, 92, 246, 0.1); + border-radius: 20px; + transition: all 0.4s ease; + position: relative; + overflow: hidden; +} + +.featureCard::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 4px; + background: var(--debros-gradient-secondary); + opacity: 0; + transition: opacity 0.3s ease; +} + +.featureCard:hover { + transform: translateY(-8px); + box-shadow: 0 20px 40px rgba(139, 92, 246, 0.15); + border-color: rgba(139, 92, 246, 0.3); +} + +.featureCard:hover::before { + opacity: 1; +} + +.featureCardHighlight { + background: linear-gradient(135deg, rgba(139, 92, 246, 0.05) 0%, rgba(59, 130, 246, 0.05) 100%); + border-color: rgba(139, 92, 246, 0.2); +} + +.featureCardHighlight::before { + opacity: 0.7; +} + +.featureIcon { + display: flex; + align-items: center; + justify-content: center; + width: 80px; + height: 80px; + margin: 0 auto 2rem; + background: rgba(139, 92, 246, 0.1); + border-radius: 20px; + border: 1px solid rgba(139, 92, 246, 0.2); + position: relative; + overflow: hidden; +} + +.featureIcon::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: var(--debros-gradient-secondary); + opacity: 0; + transition: opacity 0.3s ease; +} + +.featureCard:hover .featureIcon::before { + opacity: 0.1; +} + +.iconEmoji { + font-size: 2.5rem; + position: relative; + z-index: 1; +} + +.featureContent { + text-align: center; +} + +.featureTitle { + font-size: 1.4rem; + font-weight: 700; + color: var(--ifm-color-content); + margin-bottom: 1rem; + line-height: 1.3; +} + +.featureDescription { + font-size: 1rem; + color: var(--ifm-color-content-secondary); + line-height: 1.6; + margin: 0; +} + +.featureDescription strong { + color: var(--ifm-color-primary); + font-weight: 600; +} + +/* Responsive Design */ +@media screen and (max-width: 996px) { + .features { + padding: 4rem 0; + } + + .featuresTitle { + font-size: 2rem; + } + + .featuresSubtitle { + font-size: 1.1rem; + } + + .featuresGrid { + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 1.5rem; + } + + .featureCard { + padding: 2rem; + } + + .featureIcon { + width: 70px; + height: 70px; + margin-bottom: 1.5rem; + } + + .iconEmoji { + font-size: 2rem; + } + + .featureTitle { + font-size: 1.2rem; + } +} + +@media screen and (max-width: 576px) { + .featuresGrid { + grid-template-columns: 1fr; + } + + .featureCard { + padding: 1.5rem; + } + + .featuresTitle { + font-size: 1.8rem; + } } diff --git a/docs/src/css/custom.css b/docs/src/css/custom.css index 2bc6a4c..d5724bd 100644 --- a/docs/src/css/custom.css +++ b/docs/src/css/custom.css @@ -1,30 +1,260 @@ /** - * Any CSS included here will be global. The classic template - * bundles Infima by default. Infima is a CSS framework designed to - * work well for content-centric websites. + * DebrosFramework - Futuristic Dark Theme + * Custom styling for next-generation decentralized web framework */ -/* You can override the default Infima variables here. */ +@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap'); + +/* Custom CSS Variables for DebrosFramework */ :root { - --ifm-color-primary: #2e8555; - --ifm-color-primary-dark: #29784c; - --ifm-color-primary-darker: #277148; - --ifm-color-primary-darkest: #205d3b; - --ifm-color-primary-light: #33925d; - --ifm-color-primary-lighter: #359962; - --ifm-color-primary-lightest: #3cad6e; + /* Primary colors - Cyber Purple/Blue gradient */ + --ifm-color-primary: #7c3aed; + --ifm-color-primary-dark: #6d28d9; + --ifm-color-primary-darker: #5b21b6; + --ifm-color-primary-darkest: #4c1d95; + --ifm-color-primary-light: #8b5cf6; + --ifm-color-primary-lighter: #a78bfa; + --ifm-color-primary-lightest: #c4b5fd; + + /* Accent colors */ + --debros-accent-cyan: #06b6d4; + --debros-accent-emerald: #10b981; + --debros-accent-rose: #f43f5e; + --debros-accent-amber: #f59e0b; + + /* Background gradients */ + --debros-gradient-primary: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + --debros-gradient-secondary: linear-gradient(135deg, #7c3aed 0%, #3b82f6 50%, #06b6d4 100%); + --debros-gradient-hero: linear-gradient(135deg, #0f0f23 0%, #1a1a2e 50%, #16213e 100%); + + /* Typography */ + --ifm-font-family-base: 'Space Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; + --ifm-font-family-monospace: 'JetBrains Mono', SFMono-Regular, Menlo, Monaco, Consolas, monospace; --ifm-code-font-size: 95%; - --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); + + /* Light theme overrides */ + --ifm-background-color: #ffffff; + --ifm-background-surface-color: #f8fafc; + --docusaurus-highlighted-code-line-bg: rgba(124, 58, 237, 0.1); } -/* For readability concerns, you should choose a lighter palette in dark mode. */ +/* Dark theme - Default for DebrosFramework */ [data-theme='dark'] { - --ifm-color-primary: #25c2a0; - --ifm-color-primary-dark: #21af90; - --ifm-color-primary-darker: #1fa588; - --ifm-color-primary-darkest: #1a8870; - --ifm-color-primary-light: #29d5b0; - --ifm-color-primary-lighter: #32d8b4; - --ifm-color-primary-lightest: #4fddbf; - --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); + /* Primary colors for dark theme */ + --ifm-color-primary: #8b5cf6; + --ifm-color-primary-dark: #7c3aed; + --ifm-color-primary-darker: #6d28d9; + --ifm-color-primary-darkest: #5b21b6; + --ifm-color-primary-light: #a78bfa; + --ifm-color-primary-lighter: #c4b5fd; + --ifm-color-primary-lightest: #ddd6fe; + + /* Dark background system */ + --ifm-background-color: #0a0a0f; + --ifm-background-surface-color: #111827; + --ifm-color-content: #e5e7eb; + --ifm-color-content-secondary: #9ca3af; + + /* Navbar dark styling */ + --ifm-navbar-background-color: rgba(17, 24, 39, 0.95); + --ifm-navbar-shadow: 0 4px 28px rgba(0, 0, 0, 0.3); + + /* Sidebar dark styling */ + --ifm-menu-color: #d1d5db; + --ifm-menu-color-background-active: rgba(139, 92, 246, 0.1); + --ifm-menu-color-background-hover: rgba(139, 92, 246, 0.05); + + /* Code highlighting */ + --docusaurus-highlighted-code-line-bg: rgba(139, 92, 246, 0.2); + + /* Footer dark styling */ + --ifm-footer-background-color: #030712; + --ifm-footer-color: #9ca3af; + --ifm-footer-link-color: #d1d5db; + --ifm-footer-title-color: #f3f4f6; +} + +/* Global Typography Enhancements */ +body { + font-feature-settings: 'liga', 'kern'; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +/* Scrollbar Styling */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: transparent; +} + +::-webkit-scrollbar-thumb { + background: var(--ifm-color-primary); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: var(--ifm-color-primary-dark); +} + +/* Navbar Enhancements */ +.navbar { + backdrop-filter: blur(20px); + border-bottom: 1px solid rgba(139, 92, 246, 0.1); + transition: all 0.3s ease; +} + +.navbar__title { + font-weight: 700; + font-size: 1.25rem; + background: var(--debros-gradient-secondary); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; +} + +.navbar__item { + font-weight: 500; +} + +.navbar__link:hover { + color: var(--ifm-color-primary); +} + +/* Button Enhancements */ +.button { + font-weight: 600; + border-radius: 12px; + transition: all 0.3s ease; + font-family: var(--ifm-font-family-base); +} + +.button--primary { + background: var(--debros-gradient-secondary); + border: none; + box-shadow: 0 4px 14px rgba(139, 92, 246, 0.3); +} + +.button--primary:hover { + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(139, 92, 246, 0.4); +} + +.button--secondary { + background: transparent; + border: 2px solid var(--ifm-color-primary); + color: var(--ifm-color-primary); +} + +.button--secondary:hover { + background: var(--ifm-color-primary); + color: white; + transform: translateY(-2px); +} + +/* Code Block Enhancements */ +.prism-code { + border-radius: 12px; + border: 1px solid rgba(139, 92, 246, 0.2); + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); +} + +.code { + background: rgba(139, 92, 246, 0.1); + border: 1px solid rgba(139, 92, 246, 0.2); + border-radius: 6px; + padding: 0.2em 0.4em; + font-weight: 500; +} + +/* Table Enhancements */ +.table { + border-radius: 12px; + overflow: hidden; + border: 1px solid rgba(139, 92, 246, 0.1); +} + +.table thead tr { + background: rgba(139, 92, 246, 0.1); +} + +/* Card Components */ +.card { + background: var(--ifm-background-surface-color); + border: 1px solid rgba(139, 92, 246, 0.1); + border-radius: 16px; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); + transition: all 0.3s ease; +} + +.card:hover { + transform: translateY(-4px); + box-shadow: 0 12px 32px rgba(139, 92, 246, 0.2); +} + +/* Glow Effects */ +.glow { + position: relative; +} + +.glow::before { + content: ''; + position: absolute; + top: -2px; + left: -2px; + right: -2px; + bottom: -2px; + background: var(--debros-gradient-secondary); + border-radius: inherit; + opacity: 0; + transition: opacity 0.3s ease; + z-index: -1; +} + +.glow:hover::before { + opacity: 0.7; +} + +/* Responsive Design */ +@media screen and (max-width: 996px) { + .navbar__title { + font-size: 1.1rem; + } + + .button { + font-size: 0.9rem; + } +} + +/* Animation Utilities */ +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes pulse { + 0%, 100% { + opacity: 1; + } + 50% { + opacity: 0.5; + } +} + +.animate-fade-in-up { + animation: fadeInUp 0.6s ease-out; +} + +.animate-pulse { + animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; } diff --git a/docs/src/pages/index.module.css b/docs/src/pages/index.module.css index 9f71a5d..aff32bb 100644 --- a/docs/src/pages/index.module.css +++ b/docs/src/pages/index.module.css @@ -1,23 +1,449 @@ /** - * CSS files with the .module.css suffix will be treated as CSS modules - * and scoped locally. + * DebrosFramework Landing Page Styles + * Futuristic design with cyber-aesthetic */ -.heroBanner { - padding: 4rem 0; +/* Hero Section */ +.hero { + position: relative; + min-height: 100vh; + display: flex; + align-items: center; + background: var(--debros-gradient-hero); + overflow: hidden; +} + +.heroBackground { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 0; +} + +.gridPattern { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-image: + linear-gradient(rgba(139, 92, 246, 0.1) 1px, transparent 1px), + linear-gradient(90deg, rgba(139, 92, 246, 0.1) 1px, transparent 1px); + background-size: 50px 50px; + animation: gridMove 20s linear infinite; +} + +@keyframes gridMove { + 0% { + transform: translate(0, 0); + } + 100% { + transform: translate(50px, 50px); + } +} + +.glowOrb1, +.glowOrb2, +.glowOrb3 { + position: absolute; + border-radius: 50%; + filter: blur(60px); + animation: float 8s ease-in-out infinite; +} + +.glowOrb1 { + width: 300px; + height: 300px; + background: radial-gradient(circle, rgba(124, 58, 237, 0.3) 0%, transparent 70%); + top: 10%; + left: 10%; + animation-delay: 0s; +} + +.glowOrb2 { + width: 200px; + height: 200px; + background: radial-gradient(circle, rgba(59, 130, 246, 0.2) 0%, transparent 70%); + top: 60%; + right: 20%; + animation-delay: -3s; +} + +.glowOrb3 { + width: 250px; + height: 250px; + background: radial-gradient(circle, rgba(6, 182, 212, 0.2) 0%, transparent 70%); + bottom: 20%; + left: 50%; + animation-delay: -6s; +} + +@keyframes float { + 0%, 100% { + transform: translateY(0px) rotate(0deg); + } + 33% { + transform: translateY(-20px) rotate(120deg); + } + 66% { + transform: translateY(10px) rotate(240deg); + } +} + +.heroContent { + position: relative; + z-index: 2; text-align: center; + max-width: 800px; + margin: 0 auto; + padding: 2rem; +} + +.badge { + display: inline-block; + margin-bottom: 2rem; + animation: fadeInUp 0.6s ease-out; +} + +.badgeText { + background: rgba(139, 92, 246, 0.1); + border: 1px solid rgba(139, 92, 246, 0.3); + color: var(--ifm-color-primary); + padding: 0.5rem 1rem; + border-radius: 50px; + font-size: 0.9rem; + font-weight: 600; + backdrop-filter: blur(10px); +} + +.heroTitle { + font-size: 4rem; + font-weight: 800; + margin-bottom: 1.5rem; + line-height: 1.1; + animation: fadeInUp 0.6s ease-out 0.2s both; +} + +.titleGradient { + background: var(--debros-gradient-secondary); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + display: inline-block; +} + +.heroSubtitle { + font-size: 1.5rem; + font-weight: 600; + color: var(--ifm-color-primary); + margin-bottom: 1rem; + animation: fadeInUp 0.6s ease-out 0.4s both; +} + +.heroDescription { + font-size: 1.1rem; + color: var(--ifm-color-content-secondary); + margin-bottom: 3rem; + line-height: 1.6; + animation: fadeInUp 0.6s ease-out 0.6s both; +} + +.heroButtons { + display: flex; + gap: 1rem; + justify-content: center; + flex-wrap: wrap; + margin-bottom: 3rem; + animation: fadeInUp 0.6s ease-out 0.8s both; +} + +.primaryButton { position: relative; overflow: hidden; } -@media screen and (max-width: 996px) { - .heroBanner { - padding: 2rem; - } +.primaryButton::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); + transition: left 0.5s; } -.buttons { +.primaryButton:hover::before { + left: 100%; +} + +.buttonIcon { + margin-left: 0.5rem; + transition: transform 0.3s ease; +} + +.primaryButton:hover .buttonIcon { + transform: translateX(4px); +} + +.secondaryButton { + backdrop-filter: blur(10px); +} + +.quickStart { display: flex; align-items: center; justify-content: center; + gap: 1rem; + flex-wrap: wrap; + animation: fadeInUp 0.6s ease-out 1s both; +} + +.quickStartLabel { + font-size: 0.9rem; + color: var(--ifm-color-content-secondary); + font-weight: 500; +} + +.codeSnippet { + background: rgba(139, 92, 246, 0.1); + border: 1px solid rgba(139, 92, 246, 0.3); + color: var(--ifm-color-primary); + padding: 0.5rem 1rem; + border-radius: 8px; + font-family: var(--ifm-font-family-monospace); + font-size: 0.9rem; + backdrop-filter: blur(10px); +} + +/* Stats Section */ +.stats { + padding: 4rem 0; + background: var(--ifm-background-surface-color); + border-top: 1px solid rgba(139, 92, 246, 0.1); +} + +/* Video Section */ +.videoSection { + padding: 6rem 0; + background: var(--ifm-background-color); + border-top: 1px solid rgba(139, 92, 246, 0.1); +} + +.videoHeader { + text-align: center; + margin-bottom: 4rem; +} + +.videoTitle { + font-size: 2.5rem; + font-weight: 800; + background: var(--debros-gradient-secondary); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + margin-bottom: 1rem; +} + +.videoSubtitle { + font-size: 1.2rem; + color: var(--ifm-color-content-secondary); + max-width: 600px; + margin: 0 auto; + line-height: 1.6; +} + +.videoContainer { + display: grid; + grid-template-columns: 2fr 1fr; + gap: 3rem; + align-items: start; +} + +.videoWrapper { + position: relative; + width: 100%; + height: 0; + padding-bottom: 56.25%; /* 16:9 aspect ratio */ + background: var(--ifm-background-surface-color); + border-radius: 16px; + overflow: hidden; + border: 1px solid rgba(139, 92, 246, 0.2); + box-shadow: 0 8px 32px rgba(139, 92, 246, 0.1); +} + +.videoEmbed { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border: none; +} + +.videoFeatures { + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +.videoFeature { + padding: 1.5rem; + background: var(--ifm-background-surface-color); + border: 1px solid rgba(139, 92, 246, 0.1); + border-radius: 12px; + font-size: 1rem; + line-height: 1.5; + transition: all 0.3s ease; +} + +.videoFeature:hover { + transform: translateY(-2px); + box-shadow: 0 8px 24px rgba(139, 92, 246, 0.15); + border-color: rgba(139, 92, 246, 0.3); +} + +.videoFeature strong { + color: var(--ifm-color-primary); + font-weight: 600; +} + +.statsGrid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 2rem; + max-width: 800px; + margin: 0 auto; +} + +.statItem { + text-align: center; + padding: 2rem 1rem; + background: var(--ifm-background-color); + border: 1px solid rgba(139, 92, 246, 0.1); + border-radius: 16px; + transition: all 0.3s ease; + position: relative; + overflow: hidden; +} + +.statItem::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 3px; + background: var(--debros-gradient-secondary); +} + +.statItem:hover { + transform: translateY(-8px); + box-shadow: 0 16px 40px rgba(139, 92, 246, 0.2); +} + +.statNumber { + font-size: 2.5rem; + font-weight: 800; + color: var(--ifm-color-primary); + margin-bottom: 0.5rem; + font-family: var(--ifm-font-family-base); +} + +.statLabel { + font-size: 1rem; + font-weight: 600; + color: var(--ifm-color-content-secondary); + text-transform: uppercase; + letter-spacing: 0.5px; +} + +/* Responsive Design */ +@media screen and (max-width: 996px) { + .hero { + min-height: 80vh; + } + + .heroTitle { + font-size: 2.5rem; + } + + .heroSubtitle { + font-size: 1.2rem; + } + + .heroDescription { + font-size: 1rem; + } + + .heroButtons { + flex-direction: column; + align-items: center; + } + + .quickStart { + flex-direction: column; + gap: 0.5rem; + } + + .statsGrid { + grid-template-columns: repeat(2, 1fr); + gap: 1rem; + } + + .videoSection { + padding: 4rem 0; + } + + .videoTitle { + font-size: 2rem; + } + + .videoSubtitle { + font-size: 1.1rem; + } + + .videoContainer { + grid-template-columns: 1fr; + gap: 2rem; + } + + .glowOrb1, + .glowOrb2, + .glowOrb3 { + width: 150px; + height: 150px; + } +} + +@media screen and (max-width: 576px) { + .heroTitle { + font-size: 2rem; + } + + .statsGrid { + grid-template-columns: 1fr; + } + + .statItem { + padding: 1.5rem 1rem; + } + + .statNumber { + font-size: 2rem; + } + + .videoTitle { + font-size: 1.8rem; + } + + .videoFeatures { + gap: 1rem; + } + + .videoFeature { + padding: 1rem; + } } diff --git a/docs/src/pages/index.tsx b/docs/src/pages/index.tsx index 2e006d1..3800214 100644 --- a/docs/src/pages/index.tsx +++ b/docs/src/pages/index.tsx @@ -8,24 +8,128 @@ import Heading from '@theme/Heading'; import styles from './index.module.css'; -function HomepageHeader() { +function HeroSection() { const {siteConfig} = useDocusaurusContext(); return ( -
+
+
+
+
+
+
+
+
- - {siteConfig.title} - -

{siteConfig.tagline}

-
- - Docusaurus Tutorial - 5min โฑ๏ธ - +
+
+ ๐Ÿš€ Beta Release +
+ + + Debros Network + + +

+ {siteConfig.tagline} +

+ +

+ Build scalable, decentralized applications with ease using our next-generation + framework powered by OrbitDB, IPFS, and cutting-edge web technologies. +

+ +
+ + Get Started + โ†’ + + + + View Documentation + +
+ +
+ Quick Start: + + npm create debros-app my-app + +
-
+
+ ); +} + +function StatsSection() { + return ( +
+
+
+
+
100%
+
TypeScript
+
+
+
Zero
+
Config
+
+
+
โˆž
+
Scalability
+
+
+
๐ŸŒ
+
Decentralized
+
+
+
+
+ ); +} + +function VideoSection() { + return ( +
+
+
+ + See Debros Network in Action + +

+ Watch our comprehensive tutorial to learn how to build your first decentralized application +

+
+
+
+ {/* Replace VIDEO_ID_HERE with your actual YouTube video ID */} + +
+
+
+ ๐Ÿš€ Quick Start: Get up and running in under 5 minutes +
+
+ ๐Ÿ“š Step-by-Step: Follow along with detailed explanations +
+
+ ๐Ÿ”ง Best Practices: Learn the recommended patterns and approaches +
+
+
+
+
); } @@ -33,10 +137,12 @@ export default function Home(): ReactNode { const {siteConfig} = useDocusaurusContext(); return ( - + title={siteConfig.title} + description="Next-Generation Decentralized Web Framework - Build scalable, decentralized applications with ease using Debros Network"> + +
+
diff --git a/jest.integration.config.cjs b/jest.integration.config.cjs new file mode 100644 index 0000000..0e523d3 --- /dev/null +++ b/jest.integration.config.cjs @@ -0,0 +1,19 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + roots: ['/tests/real-integration'], + testMatch: ['**/tests/**/*.test.ts'], + transform: { + '^.+\\.ts$': [ + 'ts-jest', + { + isolatedModules: true, + }, + ], + }, + testTimeout: 120000, // 2 minutes for integration tests + verbose: true, + setupFilesAfterEnv: ['/tests/real-integration/blog-scenario/tests/setup.ts'], + maxWorkers: 1, // Run tests sequentially for integration tests + collectCoverage: false, // Skip coverage for integration tests +}; diff --git a/package.json b/package.json index b4b84cb..9e02209 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@debros/network", - "version": "0.5.0-beta", - "description": "Debros network core functionality for IPFS, libp2p and OrbitDB", + "version": "0.5.1-beta", + "description": "DebrosFramework - A powerful Node.js framework providing ORM-like abstraction over OrbitDB and IPFS for building scalable decentralized applications", "type": "module", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -20,16 +20,23 @@ "format": "prettier --write \"**/*.{ts,js,json,md}\"", "lint:fix": "npx eslint src --fix", "test:unit": "jest tests/unit", - "test:blog-integration": "tsx tests/real-integration/blog-scenario/scenarios/BlogTestRunner.ts", + "test:blog-integration": "jest --config=jest.integration.config.cjs tests/real-integration/blog-scenario/tests", "test:real": "docker-compose -f tests/real-integration/blog-scenario/docker/docker-compose.blog.yml up --build --abort-on-container-exit" }, "keywords": [ - "ipfs", - "libp2p", - "orbitdb", + "debros", + "framework", + "orm", "decentralized", + "ipfs", + "orbitdb", "p2p", - "debros" + "typescript", + "models", + "query-builder", + "sharding", + "dapp", + "distributed" ], "author": "Debros", "license": "gnu-gpl-v3.0", @@ -69,6 +76,7 @@ ] }, "devDependencies": { + "axios": "^1.6.0", "@eslint/js": "^9.24.0", "@jest/globals": "^30.0.1", "@orbitdb/core-types": "^1.0.14", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a004fb7..942cabe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -111,6 +111,9 @@ importers: '@typescript-eslint/parser': specifier: ^8.29.0 version: 8.29.0(eslint@9.24.0)(typescript@5.8.2) + axios: + specifier: ^1.6.0 + version: 1.8.4(debug@4.4.0) eslint: specifier: ^9.24.0 version: 9.24.0 diff --git a/src/framework/DebrosFramework.ts b/src/framework/DebrosFramework.ts index 0ad0372..9d88f61 100644 --- a/src/framework/DebrosFramework.ts +++ b/src/framework/DebrosFramework.ts @@ -132,7 +132,7 @@ export class DebrosFramework { this.status = { initialized: false, healthy: false, - version: '1.0.0', // This would come from package.json + version: '0.5.0-beta', // This would come from package.json environment: this.config.environment || 'development', services: { orbitdb: 'disconnected', @@ -522,9 +522,8 @@ export class DebrosFramework { this.status.services.pubsub = this.pubsubManager ? 'active' : 'inactive'; // Overall health check - only require core services to be healthy - const coreServicesHealthy = - this.status.services.orbitdb === 'connected' && - this.status.services.ipfs === 'connected'; + const coreServicesHealthy = + this.status.services.orbitdb === 'connected' && this.status.services.ipfs === 'connected'; this.status.healthy = this.initialized && coreServicesHealthy; } catch (error) { @@ -616,7 +615,7 @@ export class DebrosFramework { return { healthy: this.status.healthy, services: { ...this.status.services }, - lastCheck: this.status.lastHealthCheck + lastCheck: this.status.lastHealthCheck, }; } diff --git a/src/framework/index.ts b/src/framework/index.ts index 4c09c4d..16966ac 100644 --- a/src/framework/index.ts +++ b/src/framework/index.ts @@ -78,8 +78,8 @@ export type { FieldConfig, RelationshipConfig, ModelConfig, ValidationError } fr // export { ValidationError } from './types/models'; // Already exported above // Version information -export const FRAMEWORK_VERSION = '1.0.0'; -export const API_VERSION = '1.0'; +export const FRAMEWORK_VERSION = '0.5.0-beta'; +export const API_VERSION = '0.5'; // Feature flags for conditional exports export const FEATURES = { diff --git a/src/framework/models/BaseModel.ts b/src/framework/models/BaseModel.ts index ec1f07f..66400ee 100644 --- a/src/framework/models/BaseModel.ts +++ b/src/framework/models/BaseModel.ts @@ -22,14 +22,19 @@ export abstract class BaseModel { constructor(data: any = {}) { // Generate ID first this.id = this.generateId(); - + // Apply field defaults first this.applyFieldDefaults(); - + // Then apply provided data, but only for properties that are explicitly provided if (data && typeof data === 'object') { Object.keys(data).forEach((key) => { - if (key !== '_loadedRelations' && key !== '_isDirty' && key !== '_isNew' && data[key] !== undefined) { + if ( + key !== '_loadedRelations' && + key !== '_isDirty' && + key !== '_isNew' && + data[key] !== undefined + ) { // Always set directly - the Field decorator's setter will handle validation and transformation try { (this as any)[key] = data[key]; @@ -41,28 +46,27 @@ export abstract class BaseModel { } } }); - + // Mark as existing if it has an ID in the data if (data.id) { this._isNew = false; } } - + // Remove any instance properties that might shadow prototype getters this.cleanupShadowingProperties(); - } private cleanupShadowingProperties(): void { const modelClass = this.constructor as typeof BaseModel; - + // For each field, ensure no instance properties are shadowing prototype getters for (const [fieldName] of modelClass.fields) { // If there's an instance property, remove it and create a working getter if (this.hasOwnProperty(fieldName)) { const _oldValue = (this as any)[fieldName]; delete (this as any)[fieldName]; - + // Define a working getter directly on the instance Object.defineProperty(this, fieldName, { get: () => { @@ -75,21 +79,20 @@ export abstract class BaseModel { this.markFieldAsModified(fieldName); }, enumerable: true, - configurable: true + configurable: true, }); } } } - // Core CRUD operations async save(): Promise { if (this._isNew) { // Clean up any instance properties before hooks run this.cleanupShadowingProperties(); - + await this.beforeCreate(); - + // Clean up any instance properties created by hooks this.cleanupShadowingProperties(); @@ -102,11 +105,10 @@ export abstract class BaseModel { const now = Date.now(); this.setFieldValue('createdAt', now); this.setFieldValue('updatedAt', now); - + // Clean up any additional shadowing properties after setting timestamps this.cleanupShadowingProperties(); - - + // Validate after all field generation is complete await this.validate(); @@ -117,7 +119,7 @@ export abstract class BaseModel { this.clearModifications(); await this.afterCreate(); - + // Clean up any shadowing properties created during save this.cleanupShadowingProperties(); } else if (this._isDirty) { @@ -125,7 +127,7 @@ export abstract class BaseModel { // Set timestamp using Field setter this.setFieldValue('updatedAt', Date.now()); - + // Validate after hooks have run await this.validate(); @@ -135,7 +137,7 @@ export abstract class BaseModel { this.clearModifications(); await this.afterUpdate(); - + // Clean up any shadowing properties created during save this.cleanupShadowingProperties(); } @@ -168,21 +170,32 @@ export abstract class BaseModel { try { const modelClass = this as any; let data = null; - + if (modelClass.scope === 'user') { // For user-scoped models, we would need userId - for now, try global - const database = await framework.databaseManager?.getGlobalDatabase?.(modelClass.modelName || modelClass.name); + const database = await framework.databaseManager?.getGlobalDatabase?.( + modelClass.modelName || modelClass.name, + ); if (database && framework.databaseManager?.getDocument) { data = await framework.databaseManager.getDocument(database, modelClass.storeType, id); } } else { if (modelClass.sharding) { - const shard = framework.shardManager?.getShardForKey?.(modelClass.modelName || modelClass.name, id); + const shard = framework.shardManager?.getShardForKey?.( + modelClass.modelName || modelClass.name, + id, + ); if (shard && framework.databaseManager?.getDocument) { - data = await framework.databaseManager.getDocument(shard.database, modelClass.storeType, id); + data = await framework.databaseManager.getDocument( + shard.database, + modelClass.storeType, + id, + ); } } else { - const database = await framework.databaseManager?.getGlobalDatabase?.(modelClass.modelName || modelClass.name); + const database = await framework.databaseManager?.getGlobalDatabase?.( + modelClass.modelName || modelClass.name, + ); if (database && framework.databaseManager?.getDocument) { data = await framework.databaseManager.getDocument(database, modelClass.storeType, id); } @@ -195,7 +208,7 @@ export abstract class BaseModel { instance.clearModifications(); return instance; } - + return null; } catch (error) { console.error('Failed to find by ID:', error); @@ -283,16 +296,22 @@ export abstract class BaseModel { criteria: any, ): Promise { const query = new QueryBuilder(this as any); - + // Apply criteria as where clauses - Object.keys(criteria).forEach(key => { + Object.keys(criteria).forEach((key) => { query.where(key, '=', criteria[key]); }); - + const results = await query.limit(1).exec(); return results.length > 0 ? results[0] : null; } + static async count( + this: typeof BaseModel & (new (data?: any) => T), + ): Promise { + return await new QueryBuilder(this as any).count(); + } + // Relationship operations async load(relationships: string[]): Promise { const framework = this.getFrameworkInstance(); @@ -385,6 +404,11 @@ export abstract class BaseModel { // Include basic properties result.id = this.id; + // For OrbitDB docstore compatibility, also include _id field + if (modelClass.storeType === 'docstore') { + result._id = this.id; + } + // Include loaded relations this._loadedRelations.forEach((value, key) => { result[key] = value; @@ -396,7 +420,7 @@ export abstract class BaseModel { fromJSON(data: any): this { if (!data) return this; - // Set basic properties + // Set basic properties Object.keys(data).forEach((key) => { if (key !== '_loadedRelations' && key !== '_isDirty' && key !== '_isNew') { (this as any)[key] = data[key]; @@ -416,12 +440,11 @@ export abstract class BaseModel { const errors: string[] = []; const modelClass = this.constructor as typeof BaseModel; - // Validate each field using private keys (more reliable) for (const [fieldName, fieldConfig] of modelClass.fields) { const privateKey = `_${fieldName}`; const value = (this as any)[privateKey]; - + const fieldErrors = await this.validateField(fieldName, value, fieldConfig); errors.push(...fieldErrors); } @@ -435,7 +458,11 @@ export abstract class BaseModel { return result; } - private async validateField(fieldName: string, value: any, config: FieldConfig): Promise { + private async validateField( + fieldName: string, + value: any, + config: FieldConfig, + ): Promise { const errors: string[] = []; // Required validation @@ -544,18 +571,18 @@ export abstract class BaseModel { private applyFieldDefaults(): void { const modelClass = this.constructor as typeof BaseModel; - + // Ensure we have fields map if (!modelClass.fields) { return; } - + for (const [fieldName, fieldConfig] of modelClass.fields) { if (fieldConfig.default !== undefined) { const privateKey = `_${fieldName}`; const hasProperty = (this as any).hasOwnProperty(privateKey); const currentValue = (this as any)[privateKey]; - + // Always apply default value to private field if it's not set if (!hasProperty || currentValue === undefined) { // Apply default value to private field @@ -594,16 +621,29 @@ export abstract class BaseModel { getFieldValue(fieldName: string): any { // Always ensure this field's getter works properly this.ensureFieldGetter(fieldName); - + + // Try private key first const privateKey = `_${fieldName}`; - return (this as any)[privateKey]; + let value = (this as any)[privateKey]; + + // If private key is undefined, try the property getter as fallback + if (value === undefined) { + try { + value = (this as any)[fieldName]; + } catch (error) { + console.warn(`Failed to access field ${fieldName} using getter:`, error); + // Ignore errors from getter + } + } + + return value; } - + private ensureFieldGetter(fieldName: string): void { // If there's a shadowing instance property, remove it and create a working getter if (this.hasOwnProperty(fieldName)) { delete (this as any)[fieldName]; - + // Define a working getter directly on the instance Object.defineProperty(this, fieldName, { get: () => { @@ -616,7 +656,7 @@ export abstract class BaseModel { this.markFieldAsModified(fieldName); }, enumerable: true, - configurable: true + configurable: true, }); } } @@ -636,14 +676,14 @@ export abstract class BaseModel { getAllFieldValues(): Record { const modelClass = this.constructor as typeof BaseModel; const values: Record = {}; - + for (const [fieldName] of modelClass.fields) { const value = this.getFieldValue(fieldName); if (value !== undefined) { values[fieldName] = value; } } - + return values; } @@ -676,17 +716,20 @@ export abstract class BaseModel { try { if (modelClass.scope === 'user') { // For user-scoped models, we need a userId (check common field names) - const userId = (this as any).userId || (this as any).authorId || (this as any).ownerId; + const userId = + this.getFieldValue('userId') || + this.getFieldValue('authorId') || + this.getFieldValue('ownerId'); if (!userId) { throw new Error('User-scoped models must have a userId, authorId, or ownerId field'); } // Ensure user databases exist before accessing them await this.ensureUserDatabasesExist(framework, userId); - + // Ensure user databases exist before accessing them await this.ensureUserDatabasesExist(framework, userId); - + const database = await framework.databaseManager.getUserDatabase( userId, modelClass.modelName, @@ -705,7 +748,11 @@ export abstract class BaseModel { } else { // Use single global database const database = await framework.databaseManager.getGlobalDatabase(modelClass.modelName); - await framework.databaseManager.addDocument(database, modelClass.storeType, this.toJSON()); + await framework.databaseManager.addDocument( + database, + modelClass.storeType, + this.toJSON(), + ); } } } catch (error) { @@ -725,14 +772,17 @@ export abstract class BaseModel { try { if (modelClass.scope === 'user') { - const userId = (this as any).userId || (this as any).authorId || (this as any).ownerId; + const userId = + this.getFieldValue('userId') || + this.getFieldValue('authorId') || + this.getFieldValue('ownerId'); if (!userId) { throw new Error('User-scoped models must have a userId, authorId, or ownerId field'); } // Ensure user databases exist before accessing them await this.ensureUserDatabasesExist(framework, userId); - + const database = await framework.databaseManager.getUserDatabase( userId, modelClass.modelName, @@ -779,14 +829,17 @@ export abstract class BaseModel { try { if (modelClass.scope === 'user') { - const userId = (this as any).userId || (this as any).authorId || (this as any).ownerId; + const userId = + this.getFieldValue('userId') || + this.getFieldValue('authorId') || + this.getFieldValue('ownerId'); if (!userId) { throw new Error('User-scoped models must have a userId, authorId, or ownerId field'); } // Ensure user databases exist before accessing them await this.ensureUserDatabasesExist(framework, userId); - + const database = await framework.databaseManager.getUserDatabase( userId, modelClass.modelName, @@ -858,7 +911,7 @@ export abstract class BaseModel { if (!(globalThis as any).__mockDatabase) { (globalThis as any).__mockDatabase = new Map(); } - + const mockDatabase = { _data: (globalThis as any).__mockDatabase, async get(id: string) { @@ -874,7 +927,7 @@ export abstract class BaseModel { }, async all() { return Array.from(this._data.values()); - } + }, }; return { @@ -908,13 +961,13 @@ export abstract class BaseModel { }, async getAllDocuments(_database: any, _type: string) { return await mockDatabase.all(); - } + }, }, shardManager: { getShardForKey(_modelName: string, _key: string) { return { database: mockDatabase }; - } - } + }, + }, }; } return null; diff --git a/src/framework/models/decorators/Field.ts b/src/framework/models/decorators/Field.ts index 53962d6..0c307c2 100644 --- a/src/framework/models/decorators/Field.ts +++ b/src/framework/models/decorators/Field.ts @@ -7,18 +7,8 @@ export function Field(config: FieldConfig) { validateFieldConfig(config); // Handle ESM case where target might be undefined - if (!target) { - // In ESM environment, defer the decorator application - // Create a deferred setup that will be called when the class is actually used - console.warn(`Target is undefined for field:`, { - propertyKey, - propertyKeyType: typeof propertyKey, - propertyKeyValue: JSON.stringify(propertyKey), - configType: config.type, - target, - targetType: typeof target - }); - deferredFieldSetup(config, propertyKey); + if (!target || typeof target !== 'object') { + // Skip the decorator if target is not available - the field will be handled later return; } diff --git a/src/framework/services/OrbitDBService.ts b/src/framework/services/OrbitDBService.ts index 4f539a5..5562275 100644 --- a/src/framework/services/OrbitDBService.ts +++ b/src/framework/services/OrbitDBService.ts @@ -36,6 +36,15 @@ export class FrameworkOrbitDBService { } async openDatabase(name: string, type: StoreType): Promise { + console.log('FrameworkOrbitDBService.openDatabase called with:', { name, type }); + console.log('this.orbitDBService:', this.orbitDBService); + console.log('typeof this.orbitDBService.openDB:', typeof this.orbitDBService.openDB); + console.log('this.orbitDBService methods:', Object.getOwnPropertyNames(Object.getPrototypeOf(this.orbitDBService))); + + if (typeof this.orbitDBService.openDB !== 'function') { + throw new Error(`openDB is not a function. Service type: ${typeof this.orbitDBService}, methods: ${Object.getOwnPropertyNames(Object.getPrototypeOf(this.orbitDBService))}`); + } + return await this.orbitDBService.openDB(name, type); } diff --git a/system.txt b/system.txt deleted file mode 100644 index c179db6..0000000 --- a/system.txt +++ /dev/null @@ -1,1646 +0,0 @@ -DebrosFramework Development Specification -Overview -DebrosFramework is a comprehensive Node.js framework built on top of OrbitDB and IPFS that provides a model-based abstraction layer for building decentralized applications. The framework automatically handles database partitioning, global indexing, relationships, schema migrations, pinning strategies, and pub/sub communication while providing a clean, familiar API similar to traditional ORMs. -Architecture Principles -Core Design Goals - -Scalability: Handle millions of users through automatic database partitioning -Developer Experience: Provide familiar ORM-like API with decorators and relationships -Automatic Management: Handle pinning, indexing, pub/sub, and migrations automatically -Type Safety: Full TypeScript support with comprehensive type definitions -Performance: Intelligent caching and query optimization -Flexibility: Support different database types and scoping strategies - -Framework Layers -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Developer API โ”‚ โ† Models, decorators, query builder -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ Framework Core โ”‚ โ† Model management, relationships -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ Database Management โ”‚ โ† Sharding, indexing, caching -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ Existing @debros/network โ”‚ โ† IPFS/OrbitDB abstraction -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -Project Structure -@debros/network/ -โ”œโ”€โ”€ src/ -โ”‚ โ”œโ”€โ”€ framework/ # New framework code -โ”‚ โ”‚ โ”œโ”€โ”€ core/ -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ DebrosFramework.ts # Main framework class -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ ModelRegistry.ts # Model registration and management -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ DatabaseManager.ts # Database creation and management -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ConfigManager.ts # Framework configuration -โ”‚ โ”‚ โ”œโ”€โ”€ models/ -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ BaseModel.ts # Base model class -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ decorators/ # Model decorators -โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ Model.ts # @Model decorator -โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ Field.ts # @Field decorator -โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ relationships.ts # @HasMany, @BelongsTo, etc. -โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ hooks.ts # @BeforeCreate, @AfterUpdate, etc. -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ModelFactory.ts # Model instantiation -โ”‚ โ”‚ โ”œโ”€โ”€ query/ -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ QueryBuilder.ts # Main query builder -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ QueryExecutor.ts # Query execution strategies -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ QueryOptimizer.ts # Query optimization -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ QueryCache.ts # Query result caching -โ”‚ โ”‚ โ”œโ”€โ”€ relationships/ -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ RelationshipManager.ts -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ LazyLoader.ts -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ RelationshipCache.ts -โ”‚ โ”‚ โ”œโ”€โ”€ sharding/ -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ ShardManager.ts # Database sharding logic -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ HashSharding.ts # Hash-based sharding -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ UserSharding.ts # User-scoped databases -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ GlobalIndexManager.ts # Global index management -โ”‚ โ”‚ โ”œโ”€โ”€ migrations/ -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ MigrationManager.ts # Schema migration handling -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ VersionManager.ts # Document version management -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ MigrationStrategies.ts -โ”‚ โ”‚ โ”œโ”€โ”€ pinning/ -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ PinningManager.ts # Automatic pinning strategies -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ TieredPinning.ts # Tiered pinning implementation -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ PinningStrategies.ts # Various pinning strategies -โ”‚ โ”‚ โ”œโ”€โ”€ pubsub/ -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ PubSubManager.ts # Pub/sub abstraction -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ EventEmitter.ts # Model event emission -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ SubscriptionManager.ts -โ”‚ โ”‚ โ”œโ”€โ”€ cache/ -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ CacheManager.ts # Multi-level caching -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ MemoryCache.ts # In-memory cache -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ QueryCache.ts # Query result cache -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ RelationshipCache.ts # Relationship cache -โ”‚ โ”‚ โ”œโ”€โ”€ validation/ -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ SchemaValidator.ts # Schema validation -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ TypeValidator.ts # Field type validation -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ CustomValidators.ts # Custom validation rules -โ”‚ โ”‚ โ””โ”€โ”€ types/ -โ”‚ โ”‚ โ”œโ”€โ”€ framework.ts # Framework type definitions -โ”‚ โ”‚ โ”œโ”€โ”€ models.ts # Model type definitions -โ”‚ โ”‚ โ”œโ”€โ”€ decorators.ts # Decorator type definitions -โ”‚ โ”‚ โ””โ”€โ”€ queries.ts # Query type definitions -โ”‚ โ”œโ”€โ”€ db/ # Existing database service -โ”‚ โ”œโ”€โ”€ ipfs/ # Existing IPFS service -โ”‚ โ”œโ”€โ”€ orbit/ # Existing OrbitDB service -โ”‚ โ””โ”€โ”€ utils/ # Existing utilities -โ”œโ”€โ”€ examples/ -โ”‚ โ”œโ”€โ”€ basic-usage/ -โ”‚ โ”œโ”€โ”€ relationships/ -โ”‚ โ”œโ”€โ”€ sharding/ -โ”‚ โ””โ”€โ”€ migrations/ -โ”œโ”€โ”€ docs/ -โ”‚ โ”œโ”€โ”€ getting-started.md -โ”‚ โ”œโ”€โ”€ models.md -โ”‚ โ”œโ”€โ”€ relationships.md -โ”‚ โ”œโ”€โ”€ queries.md -โ”‚ โ”œโ”€โ”€ migrations.md -โ”‚ โ””โ”€โ”€ advanced.md -โ””โ”€โ”€ tests/ - โ”œโ”€โ”€ unit/ - โ”œโ”€โ”€ integration/ - โ””โ”€โ”€ performance/ -Implementation Roadmap -Phase 1: Core Model System (Weeks 1-2) -1.1 Base Model Implementation -File: src/framework/models/BaseModel.ts -typescript// Core functionality every model inherits -export abstract class BaseModel { - // Properties - public id: string; - public createdAt: number; - public updatedAt: number; - protected _loadedRelations: Map = new Map(); - protected _isDirty: boolean = false; - protected _isNew: boolean = true; - - // Static properties - static modelName: string; - static dbType: StoreType; - static scope: 'user' | 'global'; - static sharding?: ShardingConfig; - static pinning?: PinningConfig; - static fields: Map = new Map(); - static relationships: Map = new Map(); - static hooks: Map = new Map(); - - // Constructor - constructor(data: any = {}) { - this.fromJSON(data); - } - - // Core CRUD operations - async save(): Promise; - static async create(data: any): Promise; - static async get(id: string): Promise; - static async find(id: string): Promise; - async update(data: Partial): Promise; - async delete(): Promise; - - // Query operations - static where(field: string, operator: string, value: any): QueryBuilder; - static whereIn(field: string, values: any[]): QueryBuilder; - static orderBy(field: string, direction: 'asc' | 'desc'): QueryBuilder; - static limit(count: number): QueryBuilder; - static all(): Promise; - - // Relationship operations - async load(relationships: string[]): Promise; - async loadRelation(relationName: string): Promise; - - // Serialization - toJSON(): any; - fromJSON(data: any): this; - - // Validation - async validate(): Promise; - - // Hooks - async beforeCreate(): Promise; - async afterCreate(): Promise; - async beforeUpdate(): Promise; - async afterUpdate(): Promise; - async beforeDelete(): Promise; - async afterDelete(): Promise; -} -Implementation Tasks: - - Create BaseModel class with all core methods - Implement toJSON/fromJSON serialization - Add validation framework integration - Implement hook system (beforeCreate, afterUpdate, etc.) - Add dirty tracking for efficient updates - Create static method stubs (will be implemented in later phases) - -1.2 Model Decorators -File: src/framework/models/decorators/Model.ts -typescriptexport interface ModelConfig { - type?: StoreType; - scope?: 'user' | 'global'; - sharding?: ShardingConfig; - pinning?: PinningConfig; - pubsub?: PubSubConfig; - cache?: CacheConfig; - tableName?: string; -} - -export function Model(config: ModelConfig = {}) { - return function (target: T) { - // Set model configuration - target.modelName = config.tableName || target.name; - target.dbType = config.type || autoDetectType(target); - target.scope = config.scope || 'global'; - - // Register with framework - ModelRegistry.register(target.name, target, config); - - // Set up automatic database creation - DatabaseManager.scheduleCreation(target); - - return target; - }; -} - -function autoDetectType(modelClass: any): StoreType { - // Analyze model fields to suggest optimal database type - // Implementation details... -} -File: src/framework/models/decorators/Field.ts -typescriptexport interface FieldConfig { - type: 'string' | 'number' | 'boolean' | 'array' | 'object' | 'date'; - required?: boolean; - unique?: boolean; - index?: boolean | 'global'; - default?: any; - validate?: (value: any) => boolean | string; - transform?: (value: any) => any; -} - -export function Field(config: FieldConfig) { - return function (target: any, propertyKey: string) { - if (!target.constructor.fields) { - target.constructor.fields = new Map(); - } - - target.constructor.fields.set(propertyKey, config); - - // Create getter/setter with validation - const privateKey = `_${propertyKey}`; - - Object.defineProperty(target, propertyKey, { - get() { - return this[privateKey]; - }, - set(value) { - const validationResult = validateFieldValue(value, config); - if (!validationResult.valid) { - throw new ValidationError(validationResult.errors); - } - - this[privateKey] = config.transform ? config.transform(value) : value; - this._isDirty = true; - }, - enumerable: true, - configurable: true - }); - }; -} -File: src/framework/models/decorators/relationships.ts -typescriptexport interface RelationshipConfig { - type: 'belongsTo' | 'hasMany' | 'hasOne' | 'manyToMany'; - model: typeof BaseModel; - foreignKey: string; - localKey?: string; - through?: typeof BaseModel; - lazy?: boolean; -} - -export function BelongsTo(model: typeof BaseModel, foreignKey: string) { - return function (target: any, propertyKey: string) { - const config: RelationshipConfig = { - type: 'belongsTo', - model, - foreignKey, - lazy: true - }; - - registerRelationship(target, propertyKey, config); - createRelationshipProperty(target, propertyKey, config); - }; -} - -export function HasMany(model: typeof BaseModel, foreignKey: string) { - return function (target: any, propertyKey: string) { - const config: RelationshipConfig = { - type: 'hasMany', - model, - foreignKey, - lazy: true - }; - - registerRelationship(target, propertyKey, config); - createRelationshipProperty(target, propertyKey, config); - }; -} - -function createRelationshipProperty(target: any, propertyKey: string, config: RelationshipConfig) { - Object.defineProperty(target, propertyKey, { - get() { - if (!this._loadedRelations.has(propertyKey)) { - if (config.lazy) { - // Return a promise for lazy loading - return this.loadRelation(propertyKey); - } else { - throw new Error(`Relationship '${propertyKey}' not loaded. Use .load(['${propertyKey}']) first.`); - } - } - return this._loadedRelations.get(propertyKey); - }, - enumerable: true, - configurable: true - }); -} -Implementation Tasks: - - Implement @Model decorator with configuration - Create @Field decorator with validation and transformation - Implement relationship decorators (@BelongsTo, @HasMany, @HasOne, @ManyToMany) - Add hook decorators (@BeforeCreate, @AfterUpdate, etc.) - Create auto-detection logic for optimal database types - Add decorator validation and error handling - -1.3 Model Registry -File: src/framework/core/ModelRegistry.ts -typescriptexport class ModelRegistry { - private static models: Map = new Map(); - private static configs: Map = new Map(); - - static register(name: string, modelClass: typeof BaseModel, config: ModelConfig) { - this.models.set(name, modelClass); - this.configs.set(name, config); - - // Validate model configuration - this.validateModel(modelClass, config); - } - - static get(name: string): typeof BaseModel | undefined { - return this.models.get(name); - } - - static getConfig(name: string): ModelConfig | undefined { - return this.configs.get(name); - } - - static getAllModels(): Map { - return this.models; - } - - static getUserScopedModels(): Array { - return Array.from(this.models.values()).filter( - model => model.scope === 'user' - ); - } - - static getGlobalModels(): Array { - return Array.from(this.models.values()).filter( - model => model.scope === 'global' - ); - } - - private static validateModel(modelClass: typeof BaseModel, config: ModelConfig) { - // Validate model configuration - // Check for conflicts, missing requirements, etc. - } -} -Phase 2: Database Management & Sharding (Weeks 3-4) -2.1 Database Manager -File: src/framework/core/DatabaseManager.ts -typescriptexport class DatabaseManager { - private framework: DebrosFramework; - private databases: Map = new Map(); - private userMappings: Map = new Map(); - - constructor(framework: DebrosFramework) { - this.framework = framework; - } - - async initializeAllDatabases() { - // Initialize global databases - await this.initializeGlobalDatabases(); - - // Initialize system databases (user directory, etc.) - await this.initializeSystemDatabases(); - } - - async createUserDatabases(userId: string): Promise { - const userScopedModels = ModelRegistry.getUserScopedModels(); - const databases: any = {}; - - // Create mappings database first - const mappingsDB = await this.createDatabase( - `${userId}-mappings`, - 'keyvalue', - 'user' - ); - - // Create database for each user-scoped model - for (const model of userScopedModels) { - const dbName = `${userId}-${model.modelName.toLowerCase()}`; - const db = await this.createDatabase(dbName, model.dbType, 'user'); - databases[`${model.modelName.toLowerCase()}DB`] = db.address.toString(); - } - - // Store mappings - await mappingsDB.set('mappings', databases); - - // Register in global directory - await this.registerUserInDirectory(userId, mappingsDB.address.toString()); - - return new UserMappings(userId, databases); - } - - async getUserDatabase(userId: string, modelName: string): Promise { - const mappings = await this.getUserMappings(userId); - const dbAddress = mappings[`${modelName.toLowerCase()}DB`]; - - if (!dbAddress) { - throw new Error(`Database not found for user ${userId} and model ${modelName}`); - } - - return await this.openDatabase(dbAddress); - } - - async getUserMappings(userId: string): Promise { - // Check cache first - if (this.userMappings.has(userId)) { - return this.userMappings.get(userId); - } - - // Get from global directory - const directoryShards = await this.getGlobalDirectoryShards(); - const shardIndex = this.getShardIndex(userId, directoryShards.length); - const shard = directoryShards[shardIndex]; - - const mappingsAddress = await shard.get(userId); - if (!mappingsAddress) { - throw new Error(`User ${userId} not found in directory`); - } - - const mappingsDB = await this.openDatabase(mappingsAddress); - const mappings = await mappingsDB.get('mappings'); - - // Cache for future use - this.userMappings.set(userId, mappings); - - return mappings; - } - - private async createDatabase(name: string, type: StoreType, scope: string): Promise { - // Use existing OrbitDB service - return await this.framework.orbitDBService.openDB(name, type); - } - - private getShardIndex(key: string, shardCount: number): number { - // Simple hash-based sharding - let hash = 0; - for (let i = 0; i < key.length; i++) { - hash = ((hash << 5) - hash + key.charCodeAt(i)) & 0xffffffff; - } - return Math.abs(hash) % shardCount; - } -} -2.2 Shard Manager -File: src/framework/sharding/ShardManager.ts -typescriptexport interface ShardingConfig { - strategy: 'hash' | 'range' | 'user'; - count: number; - key: string; -} - -export class ShardManager { - private shards: Map = new Map(); - - async createShards(modelName: string, config: ShardingConfig): Promise { - const shards: any[] = []; - - for (let i = 0; i < config.count; i++) { - const shardName = `${modelName.toLowerCase()}-shard-${i}`; - const shard = await this.createShard(shardName, config); - shards.push(shard); - } - - this.shards.set(modelName, shards); - } - - getShardForKey(modelName: string, key: string): any { - const shards = this.shards.get(modelName); - if (!shards) { - throw new Error(`No shards found for model ${modelName}`); - } - - const shardIndex = this.calculateShardIndex(key, shards.length); - return shards[shardIndex]; - } - - getAllShards(modelName: string): any[] { - return this.shards.get(modelName) || []; - } - - private calculateShardIndex(key: string, shardCount: number): number { - // Hash-based sharding - let hash = 0; - for (let i = 0; i < key.length; i++) { - hash = ((hash << 5) - hash + key.charCodeAt(i)) & 0xffffffff; - } - return Math.abs(hash) % shardCount; - } - - private async createShard(shardName: string, config: ShardingConfig): Promise { - // Create OrbitDB database for this shard - // Implementation depends on existing OrbitDB service - } -} -Phase 3: Query System (Weeks 5-6) -3.1 Query Builder -File: src/framework/query/QueryBuilder.ts -typescriptexport class QueryBuilder { - private model: typeof BaseModel; - private conditions: QueryCondition[] = []; - private relations: string[] = []; - private sorting: SortConfig[] = []; - private limitation?: number; - private offsetValue?: number; - - constructor(model: typeof BaseModel) { - this.model = model; - } - - where(field: string, operator: string, value: any): this { - this.conditions.push({ field, operator, value }); - return this; - } - - whereIn(field: string, values: any[]): this { - return this.where(field, 'in', values); - } - - whereUserIn(userIds: string[]): this { - // Special method for user-scoped queries - this.conditions.push({ - field: 'userId', - operator: 'userIn', - value: userIds - }); - return this; - } - - orderBy(field: string, direction: 'asc' | 'desc' = 'asc'): this { - this.sorting.push({ field, direction }); - return this; - } - - limit(count: number): this { - this.limitation = count; - return this; - } - - offset(count: number): this { - this.offsetValue = count; - return this; - } - - load(relationships: string[]): this { - this.relations = relationships; - return this; - } - - async exec(): Promise { - const executor = new QueryExecutor(this.model, this); - return await executor.execute(); - } - - async first(): Promise { - const results = await this.limit(1).exec(); - return results[0] || null; - } - - async count(): Promise { - const executor = new QueryExecutor(this.model, this); - return await executor.count(); - } - - // Getters for query configuration - getConditions(): QueryCondition[] { return this.conditions; } - getRelations(): string[] { return this.relations; } - getSorting(): SortConfig[] { return this.sorting; } - getLimit(): number | undefined { return this.limitation; } - getOffset(): number | undefined { return this.offsetValue; } -} -3.2 Query Executor -File: src/framework/query/QueryExecutor.ts -typescriptexport class QueryExecutor { - private model: typeof BaseModel; - private query: QueryBuilder; - private framework: DebrosFramework; - - constructor(model: typeof BaseModel, query: QueryBuilder) { - this.model = model; - this.query = query; - this.framework = DebrosFramework.getInstance(); - } - - async execute(): Promise { - if (this.model.scope === 'user') { - return await this.executeUserScopedQuery(); - } else { - return await this.executeGlobalQuery(); - } - } - - private async executeUserScopedQuery(): Promise { - const conditions = this.query.getConditions(); - - // Check if we have user-specific filters - const userFilter = conditions.find(c => c.field === 'userId' || c.operator === 'userIn'); - - if (userFilter) { - return await this.executeUserSpecificQuery(userFilter); - } else { - // Global query on user-scoped data - use global index - return await this.executeGlobalIndexQuery(); - } - } - - private async executeUserSpecificQuery(userFilter: QueryCondition): Promise { - const userIds = userFilter.operator === 'userIn' - ? userFilter.value - : [userFilter.value]; - - const results: T[] = []; - - // Query each user's database in parallel - const promises = userIds.map(async (userId: string) => { - try { - const userDB = await this.framework.databaseManager.getUserDatabase( - userId, - this.model.modelName - ); - - return await this.queryDatabase(userDB); - } catch (error) { - console.warn(`Failed to query user ${userId} database:`, error); - return []; - } - }); - - const userResults = await Promise.all(promises); - - // Flatten and combine results - for (const userResult of userResults) { - results.push(...userResult); - } - - return this.postProcessResults(results); - } - - private async executeGlobalIndexQuery(): Promise { - // Query global index for user-scoped models - const globalIndexName = `${this.model.modelName}Index`; - const indexShards = this.framework.shardManager.getAllShards(globalIndexName); - - const results: any[] = []; - - // Query all shards in parallel - const promises = indexShards.map(shard => this.queryDatabase(shard)); - const shardResults = await Promise.all(promises); - - for (const shardResult of shardResults) { - results.push(...shardResult); - } - - // Now fetch actual documents from user databases - return await this.fetchActualDocuments(results); - } - - private async executeGlobalQuery(): Promise { - // For globally scoped models - if (this.model.sharding) { - return await this.executeShardedQuery(); - } else { - const db = await this.framework.databaseManager.getGlobalDatabase(this.model.modelName); - return await this.queryDatabase(db); - } - } - - private async queryDatabase(database: any): Promise { - // Get all documents from OrbitDB - let documents: any[]; - - if (this.model.dbType === 'eventlog') { - const iterator = database.iterator(); - documents = iterator.collect(); - } else if (this.model.dbType === 'keyvalue') { - documents = Object.values(database.all()); - } else if (this.model.dbType === 'docstore') { - documents = database.query(() => true); - } - - // Apply filters in memory - documents = this.applyFilters(documents); - - // Apply sorting - documents = this.applySorting(documents); - - // Apply limit/offset - documents = this.applyLimitOffset(documents); - - // Convert to model instances - return documents.map(doc => new this.model(doc) as T); - } - - private applyFilters(documents: any[]): any[] { - const conditions = this.query.getConditions(); - - return documents.filter(doc => { - return conditions.every(condition => { - return this.evaluateCondition(doc, condition); - }); - }); - } - - private evaluateCondition(doc: any, condition: QueryCondition): boolean { - const { field, operator, value } = condition; - const docValue = this.getNestedValue(doc, field); - - switch (operator) { - case '=': - case '==': - return docValue === value; - case '!=': - return docValue !== value; - case '>': - return docValue > value; - case '>=': - return docValue >= value; - case '<': - return docValue < value; - case '<=': - return docValue <= value; - case 'in': - return Array.isArray(value) && value.includes(docValue); - case 'contains': - return Array.isArray(docValue) && docValue.includes(value); - case 'like': - return String(docValue).toLowerCase().includes(String(value).toLowerCase()); - default: - throw new Error(`Unsupported operator: ${operator}`); - } - } - - private postProcessResults(results: T[]): T[] { - // Apply global sorting across all results - results = this.applySorting(results); - - // Apply global limit/offset - results = this.applyLimitOffset(results); - - return results; - } -} -Phase 4: Relationships & Loading (Weeks 7-8) -4.1 Relationship Manager -File: src/framework/relationships/RelationshipManager.ts -typescriptexport class RelationshipManager { - private framework: DebrosFramework; - private cache: RelationshipCache; - - constructor(framework: DebrosFramework) { - this.framework = framework; - this.cache = new RelationshipCache(); - } - - async loadRelationship( - instance: BaseModel, - relationshipName: string - ): Promise { - const relationConfig = instance.constructor.relationships.get(relationshipName); - - if (!relationConfig) { - throw new Error(`Relationship '${relationshipName}' not found`); - } - - // Check cache first - const cacheKey = this.getCacheKey(instance, relationshipName); - const cached = this.cache.get(cacheKey); - if (cached) { - return cached; - } - - let result: any; - - switch (relationConfig.type) { - case 'belongsTo': - result = await this.loadBelongsTo(instance, relationConfig); - break; - case 'hasMany': - result = await this.loadHasMany(instance, relationConfig); - break; - case 'hasOne': - result = await this.loadHasOne(instance, relationConfig); - break; - case 'manyToMany': - result = await this.loadManyToMany(instance, relationConfig); - break; - default: - throw new Error(`Unsupported relationship type: ${relationConfig.type}`); - } - - // Cache the result - this.cache.set(cacheKey, result); - - // Store in instance - instance._loadedRelations.set(relationshipName, result); - - return result; - } - - private async loadBelongsTo( - instance: BaseModel, - config: RelationshipConfig - ): Promise { - const foreignKeyValue = instance[config.foreignKey]; - - if (!foreignKeyValue) { - return null; - } - - return await config.model.get(foreignKeyValue); - } - - private async loadHasMany( - instance: BaseModel, - config: RelationshipConfig - ): Promise { - if (config.through) { - return await this.loadManyToMany(instance, config); - } - - // Direct has-many relationship - const query = config.model.where(config.foreignKey, '=', instance.id); - return await query.exec(); - } - - private async loadHasOne( - instance: BaseModel, - config: RelationshipConfig - ): Promise { - const results = await this.loadHasMany(instance, config); - return results[0] || null; - } - - private async loadManyToMany( - instance: BaseModel, - config: RelationshipConfig - ): Promise { - if (!config.through) { - throw new Error('Many-to-many relationships require a through model'); - } - - // Get junction table records - const junctionRecords = await config.through - .where(config.localKey || 'id', '=', instance.id) - .exec(); - - // Extract foreign keys - const foreignKeys = junctionRecords.map(record => record[config.foreignKey]); - - // Get related models - return await config.model.whereIn('id', foreignKeys).exec(); - } - - async eagerLoadRelationships( - instances: BaseModel[], - relationships: string[] - ): Promise { - // Load relationships for multiple instances efficiently - for (const relationshipName of relationships) { - await this.eagerLoadSingleRelationship(instances, relationshipName); - } - } - - private async eagerLoadSingleRelationship( - instances: BaseModel[], - relationshipName: string - ): Promise { - if (instances.length === 0) return; - - const firstInstance = instances[0]; - const relationConfig = firstInstance.constructor.relationships.get(relationshipName); - - if (!relationConfig) { - throw new Error(`Relationship '${relationshipName}' not found`); - } - - switch (relationConfig.type) { - case 'belongsTo': - await this.eagerLoadBelongsTo(instances, relationConfig); - break; - case 'hasMany': - await this.eagerLoadHasMany(instances, relationConfig); - break; - // Add other relationship types... - } - } - - private async eagerLoadBelongsTo( - instances: BaseModel[], - config: RelationshipConfig - ): Promise { - // Get all foreign key values - const foreignKeys = instances - .map(instance => instance[config.foreignKey]) - .filter(key => key != null); - - // Remove duplicates - const uniqueForeignKeys = [...new Set(foreignKeys)]; - - // Load all related models at once - const relatedModels = await config.model.whereIn('id', uniqueForeignKeys).exec(); - - // Create lookup map - const relatedMap = new Map(); - relatedModels.forEach(model => relatedMap.set(model.id, model)); - - // Assign to instances - instances.forEach(instance => { - const foreignKeyValue = instance[config.foreignKey]; - const related = relatedMap.get(foreignKeyValue) || null; - instance._loadedRelations.set(relationshipName, related); - }); - } -} -Phase 5: Automatic Features (Weeks 9-10) -5.1 Pinning Manager -File: src/framework/pinning/PinningManager.ts -typescriptexport class PinningManager { - private framework: DebrosFramework; - private strategies: Map = new Map(); - - constructor(framework: DebrosFramework) { - this.framework = framework; - this.initializeStrategies(); - } - - async pinDocument(model: BaseModel, document: any): Promise { - const modelClass = model.constructor as typeof BaseModel; - const pinningConfig = ModelRegistry.getConfig(modelClass.name)?.pinning; - - if (!pinningConfig) { - // Use default pinning - await this.pinToNodes(document.cid, 2); - return; - } - - const strategy = this.strategies.get(pinningConfig.strategy || 'fixed'); - if (!strategy) { - throw new Error(`Unknown pinning strategy: ${pinningConfig.strategy}`); - } - - const pinningFactor = await strategy.calculatePinningFactor(document, pinningConfig); - await this.pinToNodes(document.cid, pinningFactor); - } - - private async pinToNodes(cid: string, factor: number): Promise { - // Get available nodes from IPFS service - const availableNodes = await this.framework.ipfsService.getConnectedPeers(); - const nodeArray = Array.from(availableNodes.keys()); - - // Select nodes for pinning - const selectedNodes = this.selectPinningNodes(nodeArray, factor); - - // Pin to selected nodes - const pinPromises = selectedNodes.map(nodeId => - this.pinToSpecificNode(nodeId, cid) - ); - - await Promise.allSettled(pinPromises); - } - - private selectPinningNodes(nodes: string[], factor: number): string[] { - // Simple round-robin selection for now - // Could be enhanced with load balancing, geographic distribution, etc. - const shuffled = [...nodes].sort(() => Math.random() - 0.5); - return shuffled.slice(0, Math.min(factor, nodes.length)); - } - - private async pinToSpecificNode(nodeId: string, cid: string): Promise { - try { - // Implementation depends on your IPFS cluster setup - // This could be HTTP API calls, libp2p messages, etc. - await this.framework.ipfsService.pinOnNode(nodeId, cid); - } catch (error) { - console.warn(`Failed to pin ${cid} to node ${nodeId}:`, error); - } - } -} - -export interface PinningStrategy { - calculatePinningFactor(document: any, config: PinningConfig): Promise; -} - -export class PopularityPinningStrategy implements PinningStrategy { - async calculatePinningFactor(document: any, config: PinningConfig): Promise { - const baseFactor = config.factor || 2; - - // Increase pinning based on engagement - const likes = document.likes || 0; - const comments = document.comments || 0; - const engagement = likes + (comments * 2); - - if (engagement > 1000) return baseactor * 5; - if (engagement > 100) return baseactor * 3; - if (engagement > 10) return baseactor * 2; - - return baseFactor; - } -} -5.2 PubSub Manager -File: src/framework/pubsub/PubSubManager.ts -typescriptexport class PubSubManager { - private framework: DebrosFramework; - private subscriptions: Map> = new Map(); - - constructor(framework: DebrosFramework) { - this.framework = framework; - } - - async publishModelEvent( - model: BaseModel, - event: string, - data: any - ): Promise { - const modelClass = model.constructor as typeof BaseModel; - const pubsubConfig = ModelRegistry.getConfig(modelClass.name)?.pubsub; - - if (!pubsubConfig || !pubsubConfig.events.includes(event)) { - return; // No pub/sub configured for this event - } - - // Publish to configured channels - for (const channelTemplate of pubsubConfig.channels) { - const channel = this.resolveChannelTemplate(channelTemplate, model, data); - await this.publishToChannel(channel, { - model: modelClass.name, - event, - data, - timestamp: Date.now() - }); - } - } - - async subscribe( - channel: string, - callback: (data: any) => void - ): Promise<() => void> { - if (!this.subscriptions.has(channel)) { - this.subscriptions.set(channel, new Set()); - - // Subscribe to IPFS pubsub - await this.framework.ipfsService.pubsub.subscribe( - channel, - (message) => this.handleChannelMessage(channel, message) - ); - } - - this.subscriptions.get(channel)!.add(callback); - - // Return unsubscribe function - return () => { - const channelSubs = this.subscriptions.get(channel); - if (channelSubs) { - channelSubs.delete(callback); - if (channelSubs.size === 0) { - this.subscriptions.delete(channel); - this.framework.ipfsService.pubsub.unsubscribe(channel); - } - } - }; - } - - private resolveChannelTemplate( - template: string, - model: BaseModel, - data: any - ): string { - return template - .replace('{userId}', model.userId || 'unknown') - .replace('{modelName}', model.constructor.name) - .replace('{id}', model.id); - } - - private async publishToChannel(channel: string, data: any): Promise { - const message = JSON.stringify(data); - await this.framework.ipfsService.pubsub.publish(channel, message); - } - - private handleChannelMessage(channel: string, message: any): void { - const subscribers = this.subscriptions.get(channel); - if (!subscribers) return; - - try { - const data = JSON.parse(message.data.toString()); - subscribers.forEach(callback => { - try { - callback(data); - } catch (error) { - console.error('Error in pubsub callback:', error); - } - }); - } catch (error) { - console.error('Error parsing pubsub message:', error); - } - } -} -Phase 6: Migration System (Weeks 11-12) -6.1 Migration Manager -File: src/framework/migrations/MigrationManager.ts -typescriptexport abstract class Migration { - abstract version: number; - abstract modelName: string; - - abstract up(document: any): any; - abstract down(document: any): any; - - async validate(document: any): Promise { - // Optional validation after migration - return true; - } -} - -export class MigrationManager { - private migrations: Map = new Map(); - private framework: DebrosFramework; - - constructor(framework: DebrosFramework) { - this.framework = framework; - } - - registerMigration(migration: Migration): void { - const modelName = migration.modelName; - - if (!this.migrations.has(modelName)) { - this.migrations.set(modelName, []); - } - - const modelMigrations = this.migrations.get(modelName)!; - modelMigrations.push(migration); - - // Sort by version - modelMigrations.sort((a, b) => a.version - b.version); - } - - async migrateDocument( - document: any, - modelName: string, - targetVersion?: number - ): Promise { - const currentVersion = document._schemaVersion || 1; - const modelClass = ModelRegistry.get(modelName); - - if (!modelClass) { - throw new Error(`Model ${modelName} not found`); - } - - const finalVersion = targetVersion || modelClass.currentVersion || 1; - - if (currentVersion === finalVersion) { - return document; // No migration needed - } - - if (currentVersion > finalVersion) { - return await this.downgradeDocument(document, modelName, currentVersion, finalVersion); - } else { - return await this.upgradeDocument(document, modelName, currentVersion, finalVersion); - } - } - - private async upgradeDocument( - document: any, - modelName: string, - fromVersion: number, - toVersion: number - ): Promise { - const migrations = this.getMigrationsForModel(modelName); - let current = { ...document }; - - for (const migration of migrations) { - if (migration.version > fromVersion && migration.version <= toVersion) { - try { - current = migration.up(current); - current._schemaVersion = migration.version; - - // Validate migration result - const isValid = await migration.validate(current); - if (!isValid) { - throw new Error(`Migration validation failed for version ${migration.version}`); - } - } catch (error) { - console.error(`Migration failed at version ${migration.version}:`, error); - throw error; - } - } - } - - return current; - } - - private async downgradeDocument( - document: any, - modelName: string, - fromVersion: number, - toVersion: number - ): Promise { - const migrations = this.getMigrationsForModel(modelName).reverse(); - let current = { ...document }; - - for (const migration of migrations) { - if (migration.version <= fromVersion && migration.version > toVersion) { - try { - current = migration.down(current); - current._schemaVersion = migration.version - 1; - } catch (error) { - console.error(`Downgrade failed at version ${migration.version}:`, error); - throw error; - } - } - } - - return current; - } - - private getMigrationsForModel(modelName: string): Migration[] { - return this.migrations.get(modelName) || []; - } - - async migrateAllDocuments(modelName: string): Promise { - // Background migration of all documents for a model - const modelClass = ModelRegistry.get(modelName); - if (!modelClass) { - throw new Error(`Model ${modelName} not found`); - } - - if (modelClass.scope === 'user') { - await this.migrateUserScopedModel(modelName); - } else { - await this.migrateGlobalModel(modelName); - } - } - - private async migrateUserScopedModel(modelName: string): Promise { - // This is complex - would need to iterate through all users - // and migrate their individual databases - console.log(`Background migration for user-scoped model ${modelName} not implemented`); - } - - private async migrateGlobalModel(modelName: string): Promise { - // Migrate documents in global database - const db = await this.framework.databaseManager.getGlobalDatabase(modelName); - // Implementation depends on database type and migration strategy - } -} - -// Example migration -export class PostAddMediaMigration extends Migration { - version = 2; - modelName = 'Post'; - - up(document: any): any { - return { - ...document, - mediaCIDs: [], // Add new field - _schemaVersion: 2 - }; - } - - down(document: any): any { - const { mediaCIDs, ...rest } = document; - return { - ...rest, - _schemaVersion: 1 - }; - } -} -Phase 7: Framework Integration (Weeks 13-14) -7.1 Main Framework Class -File: src/framework/core/DebrosFramework.ts -typescriptexport class DebrosFramework { - private static instance: DebrosFramework; - - public databaseManager: DatabaseManager; - public shardManager: ShardManager; - public queryExecutor: QueryExecutor; - public relationshipManager: RelationshipManager; - public pinningManager: PinningManager; - public pubsubManager: PubSubManager; - public migrationManager: MigrationManager; - public cacheManager: CacheManager; - - public ipfsService: any; - public orbitDBService: any; - - private initialized: boolean = false; - - constructor(config: FrameworkConfig) { - this.databaseManager = new DatabaseManager(this); - this.shardManager = new ShardManager(); - this.relationshipManager = new RelationshipManager(this); - this.pinningManager = new PinningManager(this); - this.pubsubManager = new PubSubManager(this); - this.migrationManager = new MigrationManager(this); - this.cacheManager = new CacheManager(config.cache); - - // Use existing services - this.ipfsService = ipfsService; - this.orbitDBService = orbitDBService; - } - - static getInstance(config?: FrameworkConfig): DebrosFramework { - if (!DebrosFramework.instance) { - if (!config) { - throw new Error('Framework not initialized. Provide config on first call.'); - } - DebrosFramework.instance = new DebrosFramework(config); - } - return DebrosFramework.instance; - } - - async initialize(models: Array = []): Promise { - if (this.initialized) { - return; - } - - // Initialize underlying services - await this.ipfsService.init(); - await this.orbitDBService.init(); - - // Register models - models.forEach(model => { - if (!ModelRegistry.get(model.name)) { - // Auto-register models that weren't registered via decorators - ModelRegistry.register(model.name, model, {}); - } - }); - - // Initialize databases - await this.databaseManager.initializeAllDatabases(); - - // Create shards for global models - const globalModels = ModelRegistry.getGlobalModels(); - for (const model of globalModels) { - if (model.sharding) { - await this.shardManager.createShards(model.name, model.sharding); - } - } - - // Set up model stores - await this.setupModelStores(); - - // Set up automatic event handling - await this.setupEventHandling(); - - this.initialized = true; - } - - async createUser(userData: any): Promise { - return await this.databaseManager.createUserDatabases(userData.id); - } - - async getUser(userId: string): Promise { - return await this.databaseManager.getUserMappings(userId); - } - - private async setupModelStores(): Promise { - const allModels = ModelRegistry.getAllModels(); - - for (const [modelName, modelClass] of allModels) { - // Set the store for each model - if (modelClass.scope === 'global') { - if (modelClass.sharding) { - // Sharded global model - const shards = this.shardManager.getAllShards(modelName); - modelClass.setShards(shards); - } else { - // Single global database - const db = await this.databaseManager.getGlobalDatabase(modelName); - modelClass.setStore(db); - } - } - // User-scoped models get their stores dynamically per query - } - } - - private async setupEventHandling(): Promise { - // Set up automatic pub/sub and pinning for model events - const allModels = ModelRegistry.getAllModels(); - - for (const [modelName, modelClass] of allModels) { - // Hook into model lifecycle events - this.setupModelEventHooks(modelClass); - } - } - - private setupModelEventHooks(modelClass: typeof BaseModel): void { - const originalCreate = modelClass.create; - const originalUpdate = modelClass.prototype.update; - const originalDelete = modelClass.prototype.delete; - - // Override create method - modelClass.create = async function(data: any) { - const instance = await originalCreate.call(this, data); - - // Automatic pinning - await DebrosFramework.getInstance().pinningManager.pinDocument(instance, data); - - // Automatic pub/sub - await DebrosFramework.getInstance().pubsubManager.publishModelEvent( - instance, 'created', data - ); - - return instance; - }; - - // Override update method - modelClass.prototype.update = async function(data: any) { - const result = await originalUpdate.call(this, data); - - // Automatic pub/sub - await DebrosFramework.getInstance().pubsubManager.publishModelEvent( - this, 'updated', data - ); - - return result; - }; - - // Override delete method - modelClass.prototype.delete = async function() { - const result = await originalDelete.call(this); - - // Automatic pub/sub - await DebrosFramework.getInstance().pubsubManager.publishModelEvent( - this, 'deleted', {} - ); - - return result; - }; - } - - async stop(): Promise { - await this.orbitDBService.stop(); - await this.ipfsService.stop(); - this.initialized = false; - } -} - -export interface FrameworkConfig { - cache?: CacheConfig; - defaultPinning?: PinningConfig; - autoMigration?: boolean; -} -Testing Strategy -Unit Tests - -Model Tests: Test model creation, validation, serialization -Decorator Tests: Test all decorators work correctly -Query Tests: Test query builder and execution -Relationship Tests: Test all relationship types -Migration Tests: Test schema migrations -Sharding Tests: Test shard distribution and querying - -Integration Tests - -End-to-End Scenarios: Complete user workflows -Cross-Model Tests: Complex queries across multiple models -Performance Tests: Large dataset handling -Failure Recovery: Network failures, node failures - -Performance Tests - -Scalability Tests: Test with millions of documents -Query Performance: Benchmark query execution times -Memory Usage: Monitor memory consumption -Concurrent Access: Test multiple simultaneous operations - -Documentation Requirements -Developer Documentation - -Getting Started Guide: Basic setup and first model -Model Guide: Comprehensive model documentation -Relationships Guide: All relationship types with examples -Query Guide: Complete query API documentation -Migration Guide: Schema evolution patterns -Advanced Features: Sharding, pinning, pub/sub customization - -API Reference - -Complete TypeScript API documentation -All decorators with examples -Query builder methods -Framework configuration options - -Examples - -Basic blog application -Social media features -Real-time chat -Complex relationship scenarios -Migration examples - -Implementation Priorities -Critical Path (Must implement first) - -BaseModel class with basic CRUD -@Model and @Field decorators -Basic query builder (where, limit, etc.) -User database creation and management -Simple relationships (belongsTo, hasMany) - -High Priority (Implement second) - -Global indexing for cross-user queries -Automatic pinning based on model config -Query optimization and caching -Complete relationship types -Migration system - -Medium Priority (Can be added later) - -Advanced sharding strategies -Complex pinning strategies -Pub/sub system -Performance monitoring -Advanced caching - -Nice to Have (Future enhancements) - -GraphQL integration -Real-time subscriptions -Backup and recovery -Analytics and metrics -Admin dashboard - -Development Workflow -Phase Implementation - -Create feature branch for each phase -Implement core functionality with basic tests -Add comprehensive tests for the feature -Update documentation with examples -Review and integrate with main branch - -Code Quality Standards - -TypeScript strict mode enabled -100% type coverage for public APIs -Unit test coverage minimum 80% -ESLint and Prettier for code formatting -Conventional commits for changelog generation - -Testing Approach - -TDD approach where possible -Mock OrbitDB/IPFS for unit tests -Real OrbitDB for integration tests -Performance benchmarks for critical paths - -This specification provides a comprehensive roadmap for building the DebrosFramework. Each phase builds upon the previous one, allowing for iterative development and testing. The framework will provide a powerful, developer-friendly abstraction over OrbitDB while maintaining the scalability benefits of your original architecture.Crafted comprehensive dev document for AI framework implementation.7sThe user wants a comprehensive development document that can be provided to AI assistants to help build the framework. I've created a very detailed specification that covers: - -Architecture and Design Principles - Clear overview of what we're building and why -Detailed Project Structure - Complete file organization -Phase-by-Phase Implementation Plan - 7 phases over 14 weeks with specific deliverables -Complete Code Examples - Detailed implementation examples for each major component -Testing Strategy - Unit, integration, and performance testing approaches -Documentation Requirements - What docs need to be created -Implementation Priorities - What to build first vs. later -Development Workflow - How to organize the work - -The document is structured so that an AI assistant could: - -Understand the overall architecture -Implement specific components with the provided examples -Follow the testing and documentation requirements -Prioritize work appropriately - -Each phase has specific deliverables and code examples that show exactly what needs to be built. The document balances being comprehensive with being actionable - it provides enough detail that someone (or an AI) could start implementing immediately. -Key strengths of this specification: - -Concrete code examples for all major components -Clear separation of concerns between different modules -Incremental development approach that allows testing at each phase -Realistic timeline with achievable milestones -Comprehensive testing strategy to ensure quality -Scalability considerations built into the design from the start - -The document should give the user everything they need to either implement this themselves or provide to AI assistants for help with implementation.I've created a comprehensive development specification document that covers everything needed to build the DebrosFramework. This document is designed to be provided to AI assistants like Claude for implementation help. -Key Features of This Specification: -Complete Architecture Overview - -Clear design principles and goals -Detailed project structure with file organization -Integration with your existing @debros/network codebase - -Phase-by-Phase Implementation Plan (14 weeks) - -Phase 1-2: Core model system with decorators -Phase 3-4: Database management and sharding -Phase 5-6: Query system and relationships -Phase 7-8: Relationship loading and caching -Phase 9-10: Automatic pinning and pub/sub -Phase 11-12: Migration system -Phase 13-14: Framework integration and optimization - -Detailed Code Examples - -Complete BaseModel implementation -All decorator implementations (@Model, @Field, @BelongsTo, etc.) -Query builder with smart execution strategies -Relationship management across database types -Automatic pinning and pub/sub systems -Schema migration handling - -Comprehensive Implementation Details - -How user-scoped vs global databases work -Sharding strategies for millions of users -Query optimization for different scenarios -Relationship loading across database types -Automatic feature management (pinning, pub/sub, etc.) - -Developer Experience Focus -The framework provides a clean, familiar API while handling all OrbitDB complexity automatically: -typescript// Simple API that developers see -@Model({ scope: 'user', pinning: { factor: 3 } }) -class Post extends BaseModel { - @Field({ type: 'string' }) - content: string; - - @BelongsTo(User, 'userId') - author: User; -} - -// Complex operations work seamlessly -const posts = await Post - .whereUserIn(followedUsers) - .where('isPublic', '=', true) - .load(['author']) - .orderBy('createdAt', 'desc') - .limit(50) - .exec(); -Testing & Quality Assurance - -Unit, integration, and performance testing strategies -Code quality standards and workflows -Documentation requirements - -How to Use This Specification: - -For AI Assistance: Provide this entire document to Claude or other AI assistants when asking for implementation help -For Development Teams: Use as a technical specification and roadmap -For Phase Planning: Each phase has clear deliverables and can be implemented independently \ No newline at end of file diff --git a/tests/real-integration/blog-scenario/docker/blog-api-server.ts b/tests/real-integration/blog-scenario/docker/blog-api-server.ts index fab5e90..747dce3 100644 --- a/tests/real-integration/blog-scenario/docker/blog-api-server.ts +++ b/tests/real-integration/blog-scenario/docker/blog-api-server.ts @@ -112,8 +112,8 @@ class BlogAPIServer { const user = await User.create(sanitizedData); - console.log(`[${this.nodeId}] Created user: ${user.username} (${user.id})`); - res.status(201).json(user.toJSON()); + console.log(`[${this.nodeId}] Created user: ${user.getFieldValue('username')} (${user.id})`); + res.status(201).json(user); } catch (error) { next(error); } @@ -226,7 +226,7 @@ class BlogAPIServer { const category = await Category.create(sanitizedData); - console.log(`[${this.nodeId}] Created category: ${category.name} (${category.id})`); + console.log(`[${this.nodeId}] Created category: ${category.getFieldValue('name')} (${category.id})`); res.status(201).json(category); } catch (error) { next(error); @@ -276,7 +276,7 @@ class BlogAPIServer { const post = await Post.create(sanitizedData); - console.log(`[${this.nodeId}] Created post: ${post.title} (${post.id})`); + console.log(`[${this.nodeId}] Created post: ${post.getFieldValue('title')} (${post.id})`); res.status(201).json(post); } catch (error) { next(error); @@ -627,9 +627,6 @@ class BlogAPIServer { const { OrbitDBService } = await import( '../../../../src/framework/services/RealOrbitDBService' ); - const { FrameworkIPFSService, FrameworkOrbitDBService } = await import( - '../../../../src/framework/services/OrbitDBService' - ); // Initialize IPFS service const ipfsService = new IPFSService({ @@ -648,10 +645,10 @@ class BlogAPIServer { await orbitDBService.init(); console.log(`[${this.nodeId}] OrbitDB service initialized`); - // Wrap services for framework - const frameworkIPFS = new FrameworkIPFSService(ipfsService); - const frameworkOrbitDB = new FrameworkOrbitDBService(orbitDBService); - + // Debug: Check OrbitDB service methods + console.log(`[${this.nodeId}] OrbitDB service methods:`, Object.getOwnPropertyNames(Object.getPrototypeOf(orbitDBService))); + console.log(`[${this.nodeId}] Has openDB method:`, typeof orbitDBService.openDB === 'function'); + // Initialize framework this.framework = new DebrosFramework({ environment: 'test', @@ -669,8 +666,32 @@ class BlogAPIServer { }, }); + // Pass raw services to framework - it will wrap them itself await this.framework.initialize(orbitDBService, ipfsService); console.log(`[${this.nodeId}] DebrosFramework initialized successfully`); + + // Register models with framework + this.framework.registerModel(User, { + scope: 'global', + type: 'docstore' + }); + this.framework.registerModel(UserProfile, { + scope: 'global', + type: 'docstore' + }); + this.framework.registerModel(Category, { + scope: 'global', + type: 'docstore' + }); + this.framework.registerModel(Post, { + scope: 'user', + type: 'docstore' + }); + this.framework.registerModel(Comment, { + scope: 'user', + type: 'docstore' + }); + console.log(`[${this.nodeId}] Models registered with framework`); } } diff --git a/tests/real-integration/blog-scenario/models/BlogModels.ts b/tests/real-integration/blog-scenario/models/BlogModels.ts index 4ef1e07..93ee741 100644 --- a/tests/real-integration/blog-scenario/models/BlogModels.ts +++ b/tests/real-integration/blog-scenario/models/BlogModels.ts @@ -2,6 +2,79 @@ import 'reflect-metadata'; import { BaseModel } from '../../../../src/framework/models/BaseModel'; import { Model, Field, HasMany, BelongsTo, HasOne, BeforeCreate, AfterCreate } from '../../../../src/framework/models/decorators'; +// Force field registration by manually setting up field configurations +function setupFieldConfigurations() { + // User Profile fields + if (!UserProfile.fields) { + (UserProfile as any).fields = new Map(); + } + UserProfile.fields.set('userId', { type: 'string', required: true }); + UserProfile.fields.set('bio', { type: 'string', required: false }); + UserProfile.fields.set('location', { type: 'string', required: false }); + UserProfile.fields.set('website', { type: 'string', required: false }); + UserProfile.fields.set('socialLinks', { type: 'object', required: false }); + UserProfile.fields.set('interests', { type: 'array', required: false, default: [] }); + UserProfile.fields.set('createdAt', { type: 'number', required: false, default: () => Date.now() }); + UserProfile.fields.set('updatedAt', { type: 'number', required: false, default: () => Date.now() }); + + // User fields + if (!User.fields) { + (User as any).fields = new Map(); + } + User.fields.set('username', { type: 'string', required: true, unique: true }); + User.fields.set('email', { type: 'string', required: true, unique: true }); + User.fields.set('displayName', { type: 'string', required: false }); + User.fields.set('avatar', { type: 'string', required: false }); + User.fields.set('isActive', { type: 'boolean', required: false, default: true }); + User.fields.set('roles', { type: 'array', required: false, default: [] }); + User.fields.set('createdAt', { type: 'number', required: false }); + User.fields.set('lastLoginAt', { type: 'number', required: false }); + + // Category fields + if (!Category.fields) { + (Category as any).fields = new Map(); + } + Category.fields.set('name', { type: 'string', required: true, unique: true }); + Category.fields.set('slug', { type: 'string', required: true, unique: true }); + Category.fields.set('description', { type: 'string', required: false }); + Category.fields.set('color', { type: 'string', required: false }); + Category.fields.set('isActive', { type: 'boolean', required: false, default: true }); + Category.fields.set('createdAt', { type: 'number', required: false, default: () => Date.now() }); + + // Post fields + if (!Post.fields) { + (Post as any).fields = new Map(); + } + Post.fields.set('title', { type: 'string', required: true }); + Post.fields.set('slug', { type: 'string', required: true, unique: true }); + Post.fields.set('content', { type: 'string', required: true }); + Post.fields.set('excerpt', { type: 'string', required: false }); + Post.fields.set('authorId', { type: 'string', required: true }); + Post.fields.set('categoryId', { type: 'string', required: false }); + Post.fields.set('tags', { type: 'array', required: false, default: [] }); + Post.fields.set('status', { type: 'string', required: false, default: 'draft' }); + Post.fields.set('featuredImage', { type: 'string', required: false }); + Post.fields.set('isFeatured', { type: 'boolean', required: false, default: false }); + Post.fields.set('viewCount', { type: 'number', required: false, default: 0 }); + Post.fields.set('likeCount', { type: 'number', required: false, default: 0 }); + Post.fields.set('createdAt', { type: 'number', required: false }); + Post.fields.set('updatedAt', { type: 'number', required: false }); + Post.fields.set('publishedAt', { type: 'number', required: false }); + + // Comment fields + if (!Comment.fields) { + (Comment as any).fields = new Map(); + } + Comment.fields.set('content', { type: 'string', required: true }); + Comment.fields.set('postId', { type: 'string', required: true }); + Comment.fields.set('authorId', { type: 'string', required: true }); + Comment.fields.set('parentId', { type: 'string', required: false }); + Comment.fields.set('isApproved', { type: 'boolean', required: false, default: true }); + Comment.fields.set('likeCount', { type: 'number', required: false, default: 0 }); + Comment.fields.set('createdAt', { type: 'number', required: false }); + Comment.fields.set('updatedAt', { type: 'number', required: false }); +} + // User Profile Model @Model({ scope: 'global', @@ -368,4 +441,7 @@ export interface UpdatePostRequest { tags?: string[]; featuredImage?: string; isFeatured?: boolean; -} \ No newline at end of file +} + +// Initialize field configurations after all models are defined +setupFieldConfigurations();