gitflow-feature-stash: pstewart-refactor

This commit is contained in:
Patrick Stewart 2024-11-12 01:00:05 -07:00
parent 4b46016e35
commit 28f693a681
416 changed files with 53433 additions and 13 deletions

View file

@ -3,6 +3,7 @@ repository: https://github.com/protevus/platform
packages: packages:
- apps/** - apps/**
- packages/** - packages/**
- sandbox/**
- helpers/tools/** - helpers/tools/**
- examples/** - examples/**

77
docs/.gitignore vendored Normal file
View file

@ -0,0 +1,77 @@
# Generated documentation
api/
build/
site/
_site/
.dart_tool/
.pub-cache/
.pub/
doc/api/
# Temporary files
*.tmp
*.temp
*.bak
*.swp
*~
.DS_Store
Thumbs.db
# IDE files
.idea/
.vscode/
*.iml
*.iws
*.ipr
.settings/
.project
.classpath
# Build artifacts
*.html
*.pdf
*.epub
*.mobi
*.docx
*.doc
*.rtf
# Local configuration
.env
.env.local
*.local
local.*
# Documentation tools
node_modules/
package-lock.json
yarn.lock
pubspec.lock
# Generated diagrams
*.svg
*.png
*.jpg
*.jpeg
*.gif
!assets/*.svg
!assets/*.png
!assets/*.jpg
!assets/*.jpeg
!assets/*.gif
# Generated markdown
*.generated.md
*.auto.md
*.temp.md
# Coverage reports
coverage/
.coverage/
coverage.xml
*.lcov
# Log files
*.log
log/
logs/

163
docs/CHANGELOG.md Normal file
View file

@ -0,0 +1,163 @@
# Documentation Changelog
All notable changes to framework documentation will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.0.0] - 2024-01-17
### Added
- Core Documentation
* Created Getting Started Guide
* Created Laravel Compatibility Roadmap
* Created Foundation Integration Guide
* Created Testing Guide
* Created Package Integration Map
* Created README.md
* Created index.md
* Created CONTRIBUTING.md
* Created CHANGELOG.md
- Core Architecture
* Created Core Architecture documentation
* Created Core Package Specification
- Package Specifications
* Created Container Package Specification
* Created Contracts Package Specification
* Created Events Package Specification
* Created Pipeline Package Specification
* Created Support Package Specification
* Created Bus Package Specification
* Created Config Package Specification
* Created Filesystem Package Specification
* Created Model Package Specification
* Created Process Package Specification
* Created Queue Package Specification
* Created Route Package Specification
* Created Testing Package Specification
- Gap Analyses
* Created Container Gap Analysis
* Created Events Gap Analysis
* Created Pipeline Gap Analysis
* Created Bus Gap Analysis
* Created Config Gap Analysis
* Created Filesystem Gap Analysis
* Created Model Gap Analysis
* Created Process Gap Analysis
* Created Queue Gap Analysis
* Created Route Gap Analysis
* Created Testing Gap Analysis
- Integration Guides
* Created Container Feature Integration
* Created Container Migration Guide
### Documentation Features
- Complete package specifications with no placeholders
- Comprehensive gap analyses for Laravel compatibility
- Detailed integration guides and examples
- Cross-referenced documentation
- Consistent style and formatting
- Development guidelines for each package
- Testing requirements and examples
- Performance considerations
- Security guidelines
### Documentation Structure
- Organized by package
- Clear navigation
- Related documentation links
- Code examples
- Implementation status
- Development guidelines
### Documentation Standards
- No placeholders or truncation
- Complete code examples
- Working cross-references
- Proper markdown formatting
- Technical accuracy
### Contributing Guidelines
- Documentation standards
- Writing style guide
- Review process
- Development workflow
- Content organization
## Types of Changes
- `Added` for new documentation
- `Changed` for changes in existing documentation
- `Deprecated` for soon-to-be removed documentation
- `Removed` for now removed documentation
- `Fixed` for any documentation fixes
- `Security` for documentation about security updates
## Maintaining the Changelog
### Format
Each version should:
1. List version number and date
2. Group changes by type
3. List changes with bullet points
4. Reference related issues/PRs
5. Credit contributors
### Example
```markdown
## [1.1.0] - YYYY-MM-DD
### Added
- New documentation for feature X (#123)
- Guide for implementing Y (@contributor)
### Changed
- Updated Z documentation for clarity (#456)
- Improved examples in W guide (@contributor)
### Fixed
- Corrected code example in V doc (#789)
- Fixed broken links in U guide (@contributor)
```
### Version Numbers
- MAJOR version for significant documentation restructuring
- MINOR version for new documentation additions
- PATCH version for documentation fixes and updates
## Unreleased Changes
### To Be Added
- Package-specific changelogs
- Version-specific documentation
- Migration guides for each version
- API documentation
- Additional examples
### To Be Updated
- Performance guidelines
- Security considerations
- Testing strategies
- Integration patterns
- Development workflows
## Previous Versions
No previous versions. This is the initial documentation release.
## Contributors
- Initial documentation team
- Package maintainers
- Documentation reviewers
- Community contributors
## Questions?
For questions about documentation changes:
1. Review CONTRIBUTING.md
2. Check existing documentation
3. Ask in pull request
4. Update changelog as needed

280
docs/CONTRIBUTING.md Normal file
View file

@ -0,0 +1,280 @@
# Contributing to Framework Documentation
## Overview
This guide explains how to contribute to our framework documentation. We maintain comprehensive documentation covering package specifications, gap analyses, integration guides, and architectural documentation.
## Documentation Structure
### Core Documentation
1. [Getting Started Guide](getting_started.md) - Framework introduction and setup
2. [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) - Implementation timeline
3. [Foundation Integration Guide](foundation_integration_guide.md) - Integration patterns
4. [Testing Guide](testing_guide.md) - Testing approaches
5. [Package Integration Map](package_integration_map.md) - Package relationships
### Core Architecture
1. [Core Architecture](core_architecture.md) - System design and patterns
2. [Core Package Specification](core_package_specification.md) - Core implementation
### Package Documentation
Each package has:
1. Package Specification - Implementation details
2. Gap Analysis - Laravel compatibility gaps
3. Integration Guide - Package integration patterns
4. Development Guidelines - Implementation standards
## Contribution Guidelines
### 1. Documentation Standards
#### File Naming
- Use lowercase with underscores
- End with .md extension
- Be descriptive and specific
- Examples:
* package_specification.md
* gap_analysis.md
* integration_guide.md
#### File Structure
- Start with # Title
- Include Overview section
- Add Related Documentation links
- Use clear section headers
- Include code examples
- End with development guidelines
#### Content Requirements
- No placeholders or truncation
- Complete code examples
- Clear cross-references
- Proper markdown formatting
- Comprehensive coverage
### 2. Writing Style
#### Technical Writing
- Be clear and concise
- Use active voice
- Write in present tense
- Focus on technical accuracy
- Include practical examples
#### Code Examples
```dart
// Include complete, working examples
class Example {
final String name;
Example(this.name);
void demonstrate() {
print('Demonstrating: $name');
}
}
// Show usage
var example = Example('feature');
example.demonstrate();
```
#### Cross-References
- Use relative links
- Link to related docs
- Reference specific sections
- Example:
```markdown
See [Container Integration](container_package_specification.md#integration) for details.
```
### 3. Documentation Types
#### Package Specification
- Implementation details
- API documentation
- Integration examples
- Testing guidelines
- Development workflow
#### Gap Analysis
- Current implementation
- Laravel features
- Missing functionality
- Implementation plan
- Priority order
#### Integration Guide
- Integration points
- Package dependencies
- Code examples
- Best practices
- Common patterns
### 4. Review Process
#### Before Submitting
1. Check content completeness
2. Verify code examples
3. Test all links
4. Run markdown linter
5. Review formatting
#### Pull Request
1. Clear description
2. Reference related issues
3. List documentation changes
4. Include review checklist
5. Add relevant labels
#### Review Checklist
- [ ] No placeholders or truncation
- [ ] Complete code examples
- [ ] Working cross-references
- [ ] Proper formatting
- [ ] Technical accuracy
### 5. Development Workflow
#### Creating Documentation
1. Create feature branch
2. Write documentation
3. Add code examples
4. Include cross-references
5. Submit pull request
#### Updating Documentation
1. Review existing content
2. Make necessary changes
3. Update related docs
4. Verify all links
5. Submit pull request
#### Review Process
1. Technical review
2. Style review
3. Code example review
4. Cross-reference check
5. Final approval
## Style Guide
### 1. Markdown
#### Headers
```markdown
# Main Title
## Section Title
### Subsection Title
#### Minor Section
```
#### Lists
```markdown
1. Ordered Item
2. Ordered Item
- Unordered Sub-item
- Unordered Sub-item
- Unordered Item
- Unordered Item
1. Ordered Sub-item
2. Ordered Sub-item
```
#### Code Blocks
```markdown
/// Code with syntax highlighting
```dart
class Example {
void method() {
// Implementation
}
}
```
/// Inline code
Use `var` for variable declaration.
```
#### Links
```markdown
[Link Text](relative/path/to/file.md)
[Link Text](relative/path/to/file.md#section)
```
### 2. Content Organization
#### Package Documentation
1. Overview
2. Core Features
3. Integration Examples
4. Testing
5. Development Guidelines
#### Gap Analysis
1. Overview
2. Missing Features
3. Implementation Gaps
4. Priority Order
5. Next Steps
#### Integration Guide
1. Overview
2. Integration Points
3. Code Examples
4. Best Practices
5. Development Guidelines
### 3. Code Examples
#### Complete Examples
```dart
// Include all necessary imports
import 'package:framework/core.dart';
// Show complete implementation
class ServiceProvider {
final Container container;
ServiceProvider(this.container);
void register() {
container.singleton<Service>((c) =>
ServiceImplementation()
);
}
}
// Demonstrate usage
void main() {
var container = Container();
var provider = ServiceProvider(container);
provider.register();
}
```
#### Integration Examples
```dart
// Show real integration scenarios
class UserService {
final EventDispatcher events;
final Database db;
UserService(this.events, this.db);
Future<void> createUser(User user) async {
await events.dispatch(UserCreating(user));
await db.users.insert(user);
await events.dispatch(UserCreated(user));
}
}
```
## Questions?
For questions or clarification:
1. Review existing documentation
2. Check style guide
3. Ask in pull request
4. Update guidelines as needed

271
docs/README.md Normal file
View file

@ -0,0 +1,271 @@
# Framework Documentation
## Overview
This documentation covers our Dart framework implementation, including Laravel compatibility, package specifications, and architectural guides. The framework provides Laravel's powerful features and patterns while leveraging Dart's strengths.
## Documentation Structure
### Core Documentation
1. [Getting Started Guide](getting_started.md) - Framework introduction and setup
2. [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) - Implementation timeline
3. [Foundation Integration Guide](foundation_integration_guide.md) - Integration patterns
4. [Testing Guide](testing_guide.md) - Testing approaches and patterns
5. [Package Integration Map](package_integration_map.md) - Package relationships
### Core Architecture
1. [Core Architecture](core_architecture.md) - System design and patterns
- Architectural decisions
- System patterns
- Extension points
- Package interactions
### Package Documentation
#### Core Framework
1. Core Package
- [Core Package Specification](core_package_specification.md)
- [Core Architecture](core_architecture.md)
2. Container Package
- [Container Package Specification](container_package_specification.md)
- [Container Gap Analysis](container_gap_analysis.md)
- [Container Feature Integration](container_feature_integration.md)
- [Container Migration Guide](container_migration_guide.md)
3. Contracts Package
- [Contracts Package Specification](contracts_package_specification.md)
4. Events Package
- [Events Package Specification](events_package_specification.md)
- [Events Gap Analysis](events_gap_analysis.md)
5. Pipeline Package
- [Pipeline Package Specification](pipeline_package_specification.md)
- [Pipeline Gap Analysis](pipeline_gap_analysis.md)
6. Support Package
- [Support Package Specification](support_package_specification.md)
#### Infrastructure
1. Bus Package
- [Bus Package Specification](bus_package_specification.md)
- [Bus Gap Analysis](bus_gap_analysis.md)
2. Config Package
- [Config Package Specification](config_package_specification.md)
- [Config Gap Analysis](config_gap_analysis.md)
3. Filesystem Package
- [Filesystem Package Specification](filesystem_package_specification.md)
- [Filesystem Gap Analysis](filesystem_gap_analysis.md)
4. Model Package
- [Model Package Specification](model_package_specification.md)
- [Model Gap Analysis](model_gap_analysis.md)
5. Process Package
- [Process Package Specification](process_package_specification.md)
- [Process Gap Analysis](process_gap_analysis.md)
6. Queue Package
- [Queue Package Specification](queue_package_specification.md)
- [Queue Gap Analysis](queue_gap_analysis.md)
7. Route Package
- [Route Package Specification](route_package_specification.md)
- [Route Gap Analysis](route_gap_analysis.md)
8. Testing Package
- [Testing Package Specification](testing_package_specification.md)
- [Testing Gap Analysis](testing_gap_analysis.md)
## Getting Started
1. **Understanding the Framework**
```dart
// Start with these documents in order:
1. Getting Started Guide
2. Core Architecture
3. Laravel Compatibility Roadmap
4. Foundation Integration Guide
```
2. **Package Development**
```dart
// For each package:
1. Review package specification
2. Check gap analysis
3. Follow integration guide
4. Write tests
```
3. **Development Workflow**
```dart
// For each feature:
1. Review specifications
2. Write tests
3. Implement changes
4. Update documentation
```
## Key Concepts
### 1. Service Container Architecture
```dart
// Core application setup
var container = Container();
var app = Application(container)
..environment = 'production'
..basePath = Directory.current.path;
await app.boot();
```
### 2. Service Providers
```dart
class AppServiceProvider extends ServiceProvider {
@override
void register() {
// Register services
}
@override
void boot() {
// Bootstrap services
}
}
```
### 3. Package Integration
```dart
// Cross-package usage
class UserService {
final EventDispatcher events;
final Queue queue;
Future<void> process(User user) async {
await events.dispatch(UserProcessing(user));
await queue.push(ProcessUser(user));
}
}
```
## Implementation Status
### Core Framework (90%)
- Core Package (95%)
* Application lifecycle ✓
* Service providers ✓
* HTTP kernel ✓
* Console kernel ✓
* Exception handling ✓
* Needs: Performance optimizations
- Container Package (90%)
* Basic DI ✓
* Auto-wiring ✓
* Service providers ✓
* Needs: Contextual binding
### Infrastructure (80%)
- Bus Package (85%)
* Command dispatching ✓
* Command queuing ✓
* Needs: Command batching
- Config Package (80%)
* Configuration repository ✓
* Environment loading ✓
* Needs: Config caching
[Previous implementation status content remains exactly the same]
## Contributing
1. **Before Starting**
- Review relevant documentation
- Check implementation status
- Understand dependencies
- Write tests first
2. **Development Process**
```dart
// 1. Create feature branch
git checkout -b feature/package-name/feature-name
// 2. Write tests
void main() {
test('should implement feature', () {
// Test implementation
});
}
// 3. Implement feature
class Implementation {
// Feature code
}
// 4. Submit PR
// - Include tests
// - Update documentation
// - Add examples
```
3. **Code Review**
- Verify specifications
- Check test coverage
- Review documentation
- Validate performance
## Best Practices
1. **API Design**
```dart
// Follow framework patterns
class Service {
// Match framework method signatures
Future<void> handle();
Future<void> process();
}
```
2. **Testing**
```dart
// Comprehensive test coverage
void main() {
group('Feature', () {
// Unit tests
// Integration tests
// Performance tests
// Error cases
});
}
```
3. **Documentation**
```dart
/// Document framework compatibility
class Service {
/// Processes data following framework patterns.
///
/// Example:
/// ```dart
/// var service = container.make<Service>();
/// await service.process();
/// ```
Future<void> process();
}
```
## Questions?
For questions or clarification:
1. Review relevant documentation
2. Check implementation examples
3. Consult team leads
4. Update documentation as needed
## License
This framework is open-sourced software licensed under the [MIT license](../LICENSE).

231
docs/assets/README.md Normal file
View file

@ -0,0 +1,231 @@
# Documentation Assets
## Directory Structure
```
assets/
├── diagrams/ # Architecture and flow diagrams
├── images/ # Screenshots and general images
├── logos/ # Framework and package logos
└── icons/ # UI and feature icons
```
## Asset Organization
### 1. Diagrams
- Architecture diagrams
- Flow charts
- Sequence diagrams
- Component diagrams
- Class diagrams
Example naming:
```
diagrams/
├── architecture/
│ ├── system_overview.svg
│ ├── package_dependencies.svg
│ └── service_interaction.svg
├── flows/
│ ├── request_lifecycle.svg
│ ├── event_handling.svg
│ └── queue_processing.svg
└── sequences/
├── authentication_flow.svg
├── job_dispatch.svg
└── model_events.svg
```
### 2. Images
- Documentation screenshots
- Example outputs
- Visual guides
- Tutorial images
Example naming:
```
images/
├── getting_started/
│ ├── installation_step1.png
│ ├── configuration_step2.png
│ └── running_tests_step3.png
├── tutorials/
│ ├── creating_service_provider.png
│ ├── setting_up_queue.png
│ └── configuring_cache.png
└── examples/
├── api_response.png
├── console_output.png
└── test_results.png
```
### 3. Logos
- Framework logos
- Package logos
- Integration logos
- Partner logos
Example naming:
```
logos/
├── framework/
│ ├── full_logo.svg
│ ├── icon_only.svg
│ └── text_only.svg
├── packages/
│ ├── container_logo.svg
│ ├── events_logo.svg
│ └── queue_logo.svg
└── partners/
├── vendor_logo.svg
├── cloud_logo.svg
└── tools_logo.svg
```
### 4. Icons
- Feature icons
- UI elements
- Status indicators
- Action icons
Example naming:
```
icons/
├── features/
│ ├── caching.svg
│ ├── queuing.svg
│ └── routing.svg
├── status/
│ ├── success.svg
│ ├── warning.svg
│ └── error.svg
└── actions/
├── configure.svg
├── deploy.svg
└── monitor.svg
```
## Naming Conventions
1. **File Names**
- Use lowercase
- Use underscores for spaces
- Include category prefix
- Include size/variant suffix
- Examples:
* diagram_system_overview_large.svg
* screenshot_installation_step1.png
* logo_framework_dark.svg
* icon_feature_cache_16px.svg
2. **Directory Names**
- Use lowercase
- Use descriptive categories
- Group related assets
- Examples:
* diagrams/architecture/
* images/tutorials/
* logos/packages/
* icons/features/
## File Formats
1. **Diagrams**
- SVG (preferred for diagrams)
- PNG (when SVG not possible)
- Source files in separate repo
2. **Images**
- PNG (preferred for screenshots)
- JPG (for photos)
- WebP (for web optimization)
3. **Logos**
- SVG (preferred for logos)
- PNG (with multiple resolutions)
- Include source files
4. **Icons**
- SVG (preferred for icons)
- PNG (with multiple sizes)
- Include source files
## Usage Guidelines
1. **Diagrams**
- Use consistent styling
- Include source files
- Maintain aspect ratios
- Use standard colors
2. **Images**
- Optimize for web
- Use descriptive names
- Include alt text
- Maintain quality
3. **Logos**
- Follow brand guidelines
- Include all variants
- Maintain proportions
- Use vector formats
4. **Icons**
- Use consistent style
- Include multiple sizes
- Optimize for display
- Follow naming pattern
## Contributing Assets
1. **Adding New Assets**
- Follow naming conventions
- Use appropriate format
- Include source files
- Update documentation
2. **Updating Assets**
- Maintain version history
- Update all variants
- Keep source files
- Document changes
3. **Removing Assets**
- Update documentation
- Remove all variants
- Archive if needed
- Document removal
## Best Practices
1. **File Organization**
- Use correct directories
- Follow naming patterns
- Group related assets
- Maintain structure
2. **Version Control**
- Commit source files
- Track large files properly
- Document changes
- Use git LFS if needed
3. **Quality Control**
- Optimize for web
- Check resolutions
- Verify formats
- Test displays
4. **Documentation**
- Reference assets properly
- Include alt text
- Document sources
- Credit creators
## Questions?
For questions about assets:
1. Check naming conventions
2. Review directory structure
3. Consult usage guidelines
4. Ask in pull request

View file

@ -0,0 +1,187 @@
# Architecture Diagrams
## System Overview
The `system_overview.mmd` diagram shows the high-level architecture of our framework, including:
1. Core System
- Application lifecycle
- Dependency injection (Container)
- Event handling
- Pipeline processing
2. HTTP Layer
- Server handling
- HTTP kernel
- Routing
- Controllers
3. Service Layer
- Configuration
- Caching
- Queue management
- Database operations
4. Infrastructure
- Filesystem operations
- Process management
- Command bus
- Model layer
5. Testing Integration
- Test cases
- HTTP testing
- Database testing
- Event testing
## Rendering the Diagram
### Using Mermaid CLI
```bash
# Install Mermaid CLI
npm install -g @mermaid-js/mermaid-cli
# Generate SVG
mmdc -i system_overview.mmd -o system_overview.svg
# Generate PNG
mmdc -i system_overview.mmd -o system_overview.png
```
### Using Online Tools
1. Visit [Mermaid Live Editor](https://mermaid.live)
2. Copy content of system_overview.mmd
3. Export as SVG or PNG
### Using VSCode
1. Install "Markdown Preview Mermaid Support" extension
2. Open system_overview.mmd
3. Use preview to view diagram
## Modifying the Diagram
### Component Structure
```mermaid
%% Component Template
subgraph ComponentName ["Display Name"]
Node1[Node One]
Node2[Node Two]
Node1 --> Node2
end
```
### Style Definitions
```mermaid
%% Style Classes
classDef core fill:#f9f,stroke:#333,stroke-width:2px
classDef http fill:#bbf,stroke:#333,stroke-width:2px
classDef service fill:#bfb,stroke:#333,stroke-width:2px
classDef infra fill:#fbb,stroke:#333,stroke-width:2px
classDef test fill:#fff,stroke:#333,stroke-width:2px
```
### Adding Components
1. Define component in appropriate subgraph
2. Add relationships using arrows
3. Apply style class
4. Update documentation
## Component Descriptions
### Core System
- **Application**: Main entry point and lifecycle manager
- **Container**: Dependency injection container
- **Events**: Event dispatching and handling
- **Pipeline**: Request/process pipeline handling
### HTTP Layer
- **Server**: HTTP server implementation
- **Kernel**: HTTP request kernel
- **Router**: Route matching and handling
- **Controller**: Request controllers
### Service Layer
- **Config**: Configuration management
- **Cache**: Data caching
- **Queue**: Job queue management
- **Database**: Database operations
### Infrastructure
- **FileSystem**: File operations
- **Process**: Process management
- **Bus**: Command bus implementation
- **Model**: Data model layer
### Testing
- **TestCase**: Base test functionality
- **HttpTest**: HTTP testing utilities
- **DBTest**: Database testing utilities
- **EventTest**: Event testing utilities
## Relationships
### Core Dependencies
```mermaid
graph LR
App --> Container
App --> Events
App --> Pipeline
```
### Service Registration
```mermaid
graph LR
Container --> Services
Container --> Infrastructure
```
### Request Flow
```mermaid
graph LR
Server --> App
Controller --> Services
Services --> Infrastructure
```
### Event Flow
```mermaid
graph LR
Events --> Queue
Queue --> Process
Events --> Bus
```
## Best Practices
1. **Adding Components**
- Place in appropriate subgraph
- Use consistent naming
- Add clear relationships
- Apply correct style
2. **Updating Relationships**
- Keep lines clear
- Show direct dependencies
- Avoid crossing lines
- Group related flows
3. **Maintaining Styles**
- Use defined classes
- Keep consistent colors
- Maintain line weights
- Use clear labels
4. **Documentation**
- Update README.md
- Explain changes
- Document relationships
- Keep synchronized
## Questions?
For questions about architecture diagrams:
1. Check diagram documentation
2. Review Mermaid syntax
3. Consult team leads
4. Update documentation

View file

@ -0,0 +1,105 @@
graph TB
%% Core System
subgraph Core ["Core System"]
App[Application]
Container[Container]
Events[Events]
Pipeline[Pipeline]
App --> Container
App --> Events
App --> Pipeline
end
%% HTTP Layer
subgraph HTTP ["HTTP Layer"]
Server[HTTP Server]
Kernel[HTTP Kernel]
Router[Router]
Controller[Controller]
Server --> Kernel
Kernel --> Router
Router --> Controller
end
%% Service Layer
subgraph Services ["Service Layer"]
Config[Config]
Cache[Cache]
Queue[Queue]
DB[Database]
Config --> Container
Cache --> Container
Queue --> Events
DB --> Events
end
%% Infrastructure
subgraph Infrastructure ["Infrastructure"]
FileSystem[FileSystem]
Process[Process]
Bus[Command Bus]
Model[Model]
FileSystem --> Container
Process --> Queue
Bus --> Queue
Model --> Events
end
%% Request Flow
Server --> App
Controller --> Services
Services --> Infrastructure
%% Event Flow
Events --> Queue
Queue --> Process
Events --> Bus
%% Service Provider Registration
Container --> Services
Container --> Infrastructure
%% Middleware Pipeline
Pipeline --> HTTP
Pipeline --> Services
%% Testing Integration
subgraph Testing ["Testing"]
TestCase[TestCase]
HttpTest[HTTP Tests]
DBTest[DB Tests]
EventTest[Event Tests]
TestCase --> App
HttpTest --> Server
DBTest --> DB
EventTest --> Events
end
%% Style Definitions
classDef core fill:#f9f,stroke:#333,stroke-width:2px
classDef http fill:#bbf,stroke:#333,stroke-width:2px
classDef service fill:#bfb,stroke:#333,stroke-width:2px
classDef infra fill:#fbb,stroke:#333,stroke-width:2px
classDef test fill:#fff,stroke:#333,stroke-width:2px
%% Apply Styles
class App,Container,Events,Pipeline core
class Server,Kernel,Router,Controller http
class Config,Cache,Queue,DB service
class FileSystem,Process,Bus,Model infra
class TestCase,HttpTest,DBTest,EventTest test
%% Relationships
linkStyle default stroke:#333,stroke-width:2px
%% Notes
%% Core System handles application lifecycle
%% HTTP Layer processes web requests
%% Service Layer provides business functionality
%% Infrastructure provides system services
%% Testing ensures system quality

View file

@ -0,0 +1,625 @@
# Flow Diagrams
## Request Lifecycle
The `request_lifecycle.mmd` diagram shows the complete lifecycle of an HTTP request through our framework, including:
### 1. Entry Points
- Client HTTP Request
- Server Reception
- Kernel Handling
### 2. Middleware Processing
1. **Global Middleware**
- Maintenance Mode Check
- Post Size Validation
- String Trimming
- Empty to Null Conversion
2. **Route Middleware**
- Authentication
- Authorization
- Throttling
- CSRF Protection
3. **Response Middleware**
- Session Handling
- Cookie Processing
- Header Management
- Response Compression
### 3. Core Processing
1. **Route Resolution**
- Pattern Matching
- Parameter Binding
- Controller Resolution
2. **Controller Handling**
- Action Execution
- Parameter Injection
- Response Generation
### 4. Service Layer
1. **Business Logic**
- Service Processing
- Data Validation
- Business Rules
2. **Data Operations**
- Database Queries
- Cache Access
- File Operations
### 5. Event System
1. **Event Types**
- Model Events
- Custom Events
- System Events
2. **Event Processing**
- Synchronous Events
- Queued Events
- Broadcast Events
## Event Processing
The `event_processing.mmd` diagram shows the complete lifecycle of events through our framework, including:
### 1. Event Sources
- System Components
- User Actions
- External Triggers
- Scheduled Tasks
### 2. Event Types
1. **Immediate Events**
- Synchronous Processing
- Direct Response
- In-Memory Handling
2. **Queued Events**
- Asynchronous Processing
- Background Jobs
- Delayed Execution
3. **Broadcast Events**
- Real-time Updates
- WebSocket Integration
- Channel Broadcasting
### 3. Processing Components
1. **Event Dispatcher**
- Event Creation
- Type Detection
- Handler Resolution
- Event Routing
2. **Queue System**
- Job Queuing
- Background Processing
- Retry Handling
- Failed Job Management
3. **Broadcaster**
- Channel Management
- Real-time Delivery
- Client Connections
- Message Formatting
4. **Event Listeners**
- Event Handling
- Business Logic
- Response Generation
- Error Handling
### 4. Integration Points
1. **Database Operations**
- Transaction Management
- Data Persistence
- State Changes
- Audit Logging
2. **Cache Operations**
- Cache Invalidation
- Cache Updates
- Performance Optimization
- State Management
3. **Event Subscribers**
- Multiple Event Handling
- Event Grouping
- Subscriber Management
- Event Filtering
### 5. Channel Types
1. **Public Channels**
- Open Access
- Public Events
- General Updates
2. **Private Channels**
- Authentication Required
- User-Specific Events
- Secure Communication
3. **Presence Channels**
- User Presence
- Online Status
- User Lists
- Real-time State
## Queue Processing
The `queue_processing.mmd` diagram shows the complete lifecycle of queued jobs through our framework, including:
### 1. Job Sources
- Application Code
- Event Listeners
- Scheduled Tasks
- External Triggers
### 2. Job States
1. **Pending**
- Initial State
- Awaiting Processing
- In Queue
2. **Reserved**
- Worker Assigned
- Being Processed
- Locked for Processing
3. **Released**
- Failed Attempt
- Ready for Retry
- Back in Queue
4. **Failed**
- Max Retries Exceeded
- Permanent Failure
- Requires Attention
### 3. Processing Components
1. **Queue Manager**
- Job Registration
- Queue Selection
- Job Scheduling
- State Management
2. **Queue Worker**
- Job Processing
- Error Handling
- Retry Management
- Resource Cleanup
3. **Job Handler**
- Business Logic
- Data Processing
- Event Dispatching
- Error Reporting
4. **Failed Jobs**
- Failure Logging
- Retry Tracking
- Error Analysis
- Admin Notification
### 4. Integration Points
1. **Database Operations**
- Job Storage
- State Tracking
- Transaction Management
- Failure Logging
2. **Event System**
- Job Events
- Status Updates
- Error Notifications
- Progress Tracking
3. **Queue Monitor**
- Health Checks
- Performance Metrics
- Worker Status
- Error Rates
### 5. Processing Types
1. **Immediate Processing**
- Direct Execution
- No Delay
- Synchronous Option
2. **Delayed Processing**
- Scheduled Execution
- Time-based Delays
- Future Processing
3. **Batch Processing**
- Multiple Jobs
- Grouped Execution
- Bulk Operations
4. **Chained Processing**
- Sequential Jobs
- Dependent Tasks
- Pipeline Processing
### 6. Retry Strategy
1. **Exponential Backoff**
- Increasing Delays
- Retry Limits
- Failure Thresholds
2. **Custom Delays**
- Job-specific Timing
- Conditional Delays
- Priority-based
3. **Max Attempts**
- Attempt Limits
- Failure Handling
- Final State
## Model Lifecycle
The `model_lifecycle.mmd` diagram shows the complete lifecycle of models through our framework, including:
### 1. Model Operations
1. **Creation**
- Instance Creation
- Event Dispatching
- Database Insertion
- Cache Management
2. **Retrieval**
- Cache Checking
- Database Query
- Relationship Loading
- Model Hydration
3. **Update**
- Change Tracking
- Event Dispatching
- Database Update
- Cache Invalidation
4. **Deletion**
- Event Dispatching
- Database Deletion
- Cache Clearing
- Relationship Cleanup
### 2. Event Integration
1. **Lifecycle Events**
- Creating/Created
- Updating/Updated
- Deleting/Deleted
- Retrieved/Saving
2. **Relationship Events**
- Loading Relations
- Relation Loaded
- Relation Updated
- Relation Deleted
3. **Cache Events**
- Cache Hit/Miss
- Cache Stored
- Cache Invalidated
- Cache Cleared
### 3. Processing Components
1. **Model Instance**
- Attribute Management
- Change Tracking
- Event Dispatching
- State Management
2. **Event System**
- Event Creation
- Observer Notification
- Queue Integration
- Event Broadcasting
3. **Cache Layer**
- Cache Checking
- Cache Storage
- Cache Invalidation
- Cache Strategy
4. **Database Layer**
- Query Execution
- Record Management
- Transaction Handling
- Relationship Loading
### 4. Integration Points
1. **Observer System**
- Lifecycle Hooks
- Event Handling
- State Tracking
- Custom Logic
2. **Cache Strategy**
- Read Through
- Write Behind
- Cache Invalidation
- Cache Tags
3. **Queue Integration**
- Event Queueing
- Job Processing
- Async Operations
- Retry Handling
### 5. Model States
1. **Pending**
- New Instance
- Not Persisted
- No Events
2. **Active**
- Persisted
- Trackable
- Observable
3. **Modified**
- Changes Tracked
- Events Pending
- Cache Invalid
4. **Deleted**
- Soft Deleted
- Hard Deleted
- Cache Cleared
### 6. Performance Features
1. **Eager Loading**
- Relationship Loading
- Query Optimization
- N+1 Prevention
2. **Cache Management**
- Query Cache
- Model Cache
- Relationship Cache
3. **Batch Operations**
- Bulk Insert
- Bulk Update
- Bulk Delete
## Rendering the Diagrams
### Using Mermaid CLI
```bash
# Install Mermaid CLI
npm install -g @mermaid-js/mermaid-cli
# Generate SVG
mmdc -i request_lifecycle.mmd -o request_lifecycle.svg
mmdc -i event_processing.mmd -o event_processing.svg
mmdc -i queue_processing.mmd -o queue_processing.svg
mmdc -i model_lifecycle.mmd -o model_lifecycle.svg
# Generate PNG
mmdc -i request_lifecycle.mmd -o request_lifecycle.png
mmdc -i event_processing.mmd -o event_processing.png
mmdc -i queue_processing.mmd -o queue_processing.png
mmdc -i model_lifecycle.mmd -o model_lifecycle.png
```
### Using Online Tools
1. Visit [Mermaid Live Editor](https://mermaid.live)
2. Copy content of .mmd files
3. Export as SVG or PNG
### Using VSCode
1. Install "Markdown Preview Mermaid Support" extension
2. Open .mmd files
3. Use preview to view diagrams
## Modifying the Diagrams
### Sequence Structure
```mermaid
sequenceDiagram
participant A
participant B
A->>B: Message
activate B
B-->>A: Response
deactivate B
Note over A,B: Description
```
### Style Definitions
```mermaid
style Client fill:#f9f,stroke:#333,stroke-width:2px
style Server fill:#bbf,stroke:#333,stroke-width:2px
style Kernel fill:#bbf,stroke:#333,stroke-width:2px
style Pipeline fill:#bfb,stroke:#333,stroke-width:2px
```
### Adding Components
1. Add participant declaration
2. Add message sequences
3. Add activation/deactivation
4. Add notes and descriptions
## Component Interactions
### 1. Request Processing
```mermaid
sequenceDiagram
Client->>Server: Request
Server->>Kernel: Handle
Kernel->>Pipeline: Process
```
### 2. Middleware Chain
```mermaid
sequenceDiagram
Kernel->>Pipeline: Global Middleware
Pipeline->>Pipeline: Route Middleware
Pipeline->>Pipeline: Response Middleware
```
### 3. Business Logic
```mermaid
sequenceDiagram
Controller->>Services: Process
Services->>DB: Query
Services->>Events: Dispatch
```
### 4. Event Flow
```mermaid
sequenceDiagram
Source->>Dispatcher: Dispatch Event
Dispatcher->>Queue: Queue Event
Queue->>Listeners: Process Later
```
### 5. Broadcasting
```mermaid
sequenceDiagram
Dispatcher->>Broadcast: Send Event
Broadcast->>Clients: Real-time Update
Clients-->>Broadcast: Received
```
### 6. Queue Processing
```mermaid
sequenceDiagram
Source->>Queue: Dispatch Job
Queue->>Worker: Process Job
Worker->>Handler: Execute Job
Handler-->>Worker: Job Complete
```
### 7. Model Operations
```mermaid
sequenceDiagram
Client->>Model: Create/Update/Delete
Model->>Events: Dispatch Event
Events->>Observer: Handle Event
Model->>DB: Persist Changes
Model->>Cache: Update Cache
```
## Component Details
### HTTP Layer
- **Server**: Handles raw HTTP requests
- **Kernel**: Manages request processing
- **Pipeline**: Executes middleware chain
- **Router**: Matches routes to controllers
### Processing Layer
- **Controller**: Handles business logic
- **Services**: Processes domain logic
- **Events**: Manages system events
- **Database**: Handles data persistence
### Event System
- **Dispatcher**: Central event hub
- **Queue**: Async processing
- **Broadcast**: Real-time updates
- **Listeners**: Event handlers
### Queue System
- **Manager**: Queue management
- **Worker**: Job processing
- **Handler**: Job execution
- **Monitor**: Health checks
### Model System
- **Model**: Data representation
- **Observer**: Event handling
- **Cache**: Performance layer
- **Relations**: Relationship management
### Integration Points
- **Database**: Data persistence
- **Cache**: Performance layer
- **Subscribers**: Event consumers
- **External Systems**: Third-party services
### Processing Types
- **Synchronous**: Immediate processing
- **Asynchronous**: Queued processing
- **Real-time**: Broadcast processing
- **Batch**: Grouped processing
### Event Categories
- **System Events**: Framework operations
- **Domain Events**: Business logic
- **User Events**: User actions
- **Integration Events**: External systems
## Best Practices
### 1. Adding Sequences
- Use clear message names
- Show activation state
- Add relevant notes
- Group related actions
### 2. Updating Flow
- Maintain sequence order
- Show parallel operations
- Indicate async processes
- Document timing
### 3. Maintaining Style
- Use consistent colors
- Keep clear spacing
- Add helpful notes
- Use proper arrows
### 4. Documentation
- Update README.md
- Explain changes
- Document new flows
- Keep synchronized
### 5. Event Design
- Use clear event names
- Include necessary data
- Consider async needs
- Plan broadcast strategy
### 6. Queue Management
- Set appropriate delays
- Handle failures
- Monitor queue size
- Implement retries
### 7. Broadcast Strategy
- Choose correct channels
- Manage authentication
- Handle disconnections
- Optimize payload
### 8. Error Handling
- Log failures
- Implement retries
- Notify administrators
- Maintain state
### 9. Model Operations
- Track changes
- Manage cache
- Handle events
- Optimize queries
## Questions?
For questions about flow diagrams:
1. Check diagram documentation
2. Review Mermaid syntax
3. Consult team leads
4. Update documentation

View file

@ -0,0 +1,112 @@
sequenceDiagram
participant Source as Event Source
participant Dispatcher as Event Dispatcher
participant Queue as Queue System
participant Broadcast as Broadcaster
participant Listeners as Event Listeners
participant DB as Database
participant Cache as Cache System
participant Subscribers as Event Subscribers
%% Event Creation
Source->>Dispatcher: Dispatch Event
activate Dispatcher
Note over Dispatcher: Event Created
%% Event Type Check
alt Is Queued Event
Dispatcher->>Queue: Push to Queue
activate Queue
Queue-->>Dispatcher: Queued Successfully
deactivate Queue
else Is Immediate Event
Dispatcher->>Listeners: Process Immediately
activate Listeners
end
%% Broadcasting Check
alt Should Broadcast
Dispatcher->>Broadcast: Broadcast Event
activate Broadcast
Note over Broadcast: WebSocket/Redis/Pusher
Broadcast-->>Dispatcher: Broadcast Complete
deactivate Broadcast
end
%% Database Operations
alt Has Database Operations
Dispatcher->>DB: Begin Transaction
activate DB
Note over DB: Event-related Changes
DB-->>Dispatcher: Transaction Complete
deactivate DB
end
%% Cache Operations
alt Has Cache Operations
Dispatcher->>Cache: Update Cache
activate Cache
Note over Cache: Cache Invalidation/Update
Cache-->>Dispatcher: Cache Updated
deactivate Cache
end
%% Event Subscribers
Dispatcher->>Subscribers: Notify Subscribers
activate Subscribers
Note over Subscribers: Handle Multiple Events
Subscribers-->>Dispatcher: Processing Complete
deactivate Subscribers
%% Queued Event Processing
alt Is Queued Event
Queue->>Listeners: Process Queue
activate Queue
Note over Queue: Background Processing
Listeners-->>Queue: Processing Complete
deactivate Queue
end
%% Event Listeners Processing
Note over Listeners: Process Event
Listeners-->>Dispatcher: Handling Complete
deactivate Listeners
%% Event Completion
Dispatcher-->>Source: Event Processed
deactivate Dispatcher
%% Style Definitions
style Source fill:#f9f,stroke:#333,stroke-width:2px
style Dispatcher fill:#bbf,stroke:#333,stroke-width:2px
style Queue fill:#bfb,stroke:#333,stroke-width:2px
style Broadcast fill:#fbb,stroke:#333,stroke-width:2px
style Listeners fill:#bfb,stroke:#333,stroke-width:2px
style DB fill:#fbb,stroke:#333,stroke-width:2px
style Cache fill:#fbb,stroke:#333,stroke-width:2px
style Subscribers fill:#bfb,stroke:#333,stroke-width:2px
%% Notes
Note right of Source: System Component
Note right of Dispatcher: Event Management
Note right of Queue: Async Processing
Note right of Broadcast: Real-time Updates
Note right of Listeners: Event Handlers
Note right of DB: Persistence Layer
Note right of Cache: Performance Layer
Note right of Subscribers: Event Subscribers
%% Event Types
Note over Dispatcher: Event Types:<br>1. Immediate Events<br>2. Queued Events<br>3. Broadcast Events
%% Processing Types
Note over Listeners: Processing Types:<br>1. Sync Processing<br>2. Async Processing<br>3. Batch Processing
%% Integration Points
Note over Queue: Integration:<br>1. Redis Queue<br>2. Database Queue<br>3. Memory Queue
%% Broadcast Channels
Note over Broadcast: Channels:<br>1. Public Channels<br>2. Private Channels<br>3. Presence Channels
%% Subscriber Types
Note over Subscribers: Subscriber Types:<br>1. Model Subscribers<br>2. System Subscribers<br>3. Custom Subscribers

View file

@ -0,0 +1,161 @@
sequenceDiagram
participant Client as Model Client
participant Model as Model Instance
participant Events as Event System
participant Cache as Cache Layer
participant DB as Database
participant Queue as Queue System
participant Relations as Relationship Loader
participant Observer as Model Observer
%% Model Creation
Client->>Model: Create Model
activate Model
Model->>Events: Dispatch Creating Event
activate Events
Events->>Observer: Handle Creating
Observer-->>Events: Continue
Events-->>Model: Proceed
deactivate Events
%% Database Operation
Model->>DB: Insert Record
activate DB
DB-->>Model: Record Created
deactivate DB
%% Post Creation
Model->>Events: Dispatch Created Event
activate Events
Events->>Observer: Handle Created
Observer-->>Events: Continue
Events->>Queue: Queue Events
Queue-->>Events: Queued
Events-->>Model: Complete
deactivate Events
Model-->>Client: Model Instance
deactivate Model
%% Model Retrieval
Client->>Model: Find Model
activate Model
Model->>Cache: Check Cache
activate Cache
alt Cache Hit
Cache-->>Model: Return Cached
else Cache Miss
Cache-->>Model: Not Found
Model->>DB: Query Database
activate DB
DB-->>Model: Record Found
deactivate DB
Model->>Cache: Store in Cache
Cache-->>Model: Cached
end
deactivate Cache
%% Relationship Loading
Model->>Relations: Load Relations
activate Relations
Relations->>DB: Query Relations
activate DB
DB-->>Relations: Related Records
deactivate DB
Relations-->>Model: Relations Loaded
deactivate Relations
Model-->>Client: Complete Model
deactivate Model
%% Model Update
Client->>Model: Update Model
activate Model
Model->>Events: Dispatch Updating Event
activate Events
Events->>Observer: Handle Updating
Observer-->>Events: Continue
Events-->>Model: Proceed
deactivate Events
Model->>DB: Update Record
activate DB
DB-->>Model: Record Updated
deactivate DB
Model->>Cache: Invalidate Cache
activate Cache
Cache-->>Model: Cache Cleared
deactivate Cache
Model->>Events: Dispatch Updated Event
activate Events
Events->>Observer: Handle Updated
Observer-->>Events: Continue
Events->>Queue: Queue Events
Queue-->>Events: Queued
Events-->>Model: Complete
deactivate Events
Model-->>Client: Updated Model
deactivate Model
%% Model Deletion
Client->>Model: Delete Model
activate Model
Model->>Events: Dispatch Deleting Event
activate Events
Events->>Observer: Handle Deleting
Observer-->>Events: Continue
Events-->>Model: Proceed
deactivate Events
Model->>DB: Delete Record
activate DB
DB-->>Model: Record Deleted
deactivate DB
Model->>Cache: Clear Cache
activate Cache
Cache-->>Model: Cache Cleared
deactivate Cache
Model->>Events: Dispatch Deleted Event
activate Events
Events->>Observer: Handle Deleted
Observer-->>Events: Continue
Events->>Queue: Queue Events
Queue-->>Events: Queued
Events-->>Model: Complete
deactivate Events
Model-->>Client: Deletion Confirmed
deactivate Model
%% Style Definitions
style Client fill:#f9f,stroke:#333,stroke-width:2px
style Model fill:#bbf,stroke:#333,stroke-width:2px
style Events fill:#bfb,stroke:#333,stroke-width:2px
style Cache fill:#fbb,stroke:#333,stroke-width:2px
style DB fill:#fbb,stroke:#333,stroke-width:2px
style Queue fill:#bfb,stroke:#333,stroke-width:2px
style Relations fill:#bbf,stroke:#333,stroke-width:2px
style Observer fill:#bfb,stroke:#333,stroke-width:2px
%% Notes
Note over Model: Model Lifecycle
Note over Events: Event Handling
Note over Cache: Cache Management
Note over DB: Data Persistence
Note over Queue: Async Processing
Note over Relations: Relationship Management
Note over Observer: Lifecycle Hooks
%% Model States
Note over Model: States:<br>1. Creating/Created<br>2. Retrieving/Retrieved<br>3. Updating/Updated<br>4. Deleting/Deleted
%% Event Types
Note over Events: Events:<br>1. Model Events<br>2. Relation Events<br>3. Cache Events<br>4. Queue Events
%% Cache Strategy
Note over Cache: Strategy:<br>1. Read Through<br>2. Write Behind<br>3. Cache Invalidation
%% Observer Points
Note over Observer: Hooks:<br>1. Before Events<br>2. After Events<br>3. Around Events

View file

@ -0,0 +1,119 @@
sequenceDiagram
participant Source as Job Source
participant Queue as Queue Manager
participant Worker as Queue Worker
participant Job as Job Handler
participant Events as Event System
participant DB as Database
participant Failed as Failed Jobs
participant Monitor as Queue Monitor
%% Job Creation
Source->>Queue: Dispatch Job
activate Queue
Note over Queue: Create Job Record
%% Job Configuration
alt Has Delay
Queue->>Queue: Schedule for Later
else No Delay
Queue->>Queue: Available Immediately
end
%% Worker Pickup
Queue->>Worker: Worker Picks Up Job
activate Worker
Note over Worker: Start Processing
%% Job Processing
Worker->>Job: Handle Job
activate Job
%% Database Transaction
alt Has Database Operations
Job->>DB: Begin Transaction
activate DB
Note over DB: Perform Operations
DB-->>Job: Transaction Complete
deactivate DB
end
%% Event Dispatching
alt Has Events
Job->>Events: Dispatch Events
activate Events
Note over Events: Process Events
Events-->>Job: Events Handled
deactivate Events
end
%% Job Completion Check
alt Job Succeeds
Job-->>Worker: Processing Complete
Worker-->>Queue: Job Completed
Queue->>Events: JobProcessed Event
else Job Fails
Job-->>Worker: Throws Exception
%% Retry Logic
alt Can Retry
Worker->>Queue: Release Job
Note over Queue: Increment Attempts
Queue-->>Worker: Job Released
else Max Retries Exceeded
Worker->>Failed: Move to Failed Jobs
activate Failed
Note over Failed: Log Failure
Failed-->>Worker: Failure Logged
deactivate Failed
end
end
deactivate Job
deactivate Worker
deactivate Queue
%% Queue Monitoring
Monitor->>Queue: Check Status
activate Monitor
Queue-->>Monitor: Queue Statistics
Note over Monitor: Monitor Queue Health
Monitor->>Failed: Check Failed Jobs
Failed-->>Monitor: Failed Job Count
Monitor->>Worker: Check Workers
Worker-->>Monitor: Worker Status
deactivate Monitor
%% Style Definitions
style Source fill:#f9f,stroke:#333,stroke-width:2px
style Queue fill:#bbf,stroke:#333,stroke-width:2px
style Worker fill:#bfb,stroke:#333,stroke-width:2px
style Job fill:#bfb,stroke:#333,stroke-width:2px
style Events fill:#fbb,stroke:#333,stroke-width:2px
style DB fill:#fbb,stroke:#333,stroke-width:2px
style Failed fill:#fbb,stroke:#333,stroke-width:2px
style Monitor fill:#bbf,stroke:#333,stroke-width:2px
%% Notes
Note right of Source: Application Code
Note right of Queue: Queue Management
Note right of Worker: Job Processing
Note right of Job: Business Logic
Note right of Events: Event System
Note right of DB: Data Layer
Note right of Failed: Error Handling
Note right of Monitor: Health Checks
%% Job States
Note over Queue: Job States:<br>1. Pending<br>2. Reserved<br>3. Released<br>4. Failed
%% Processing Types
Note over Worker: Processing:<br>1. Immediate<br>2. Delayed<br>3. Batched<br>4. Chained
%% Retry Strategy
Note over Failed: Retry Strategy:<br>1. Exponential Backoff<br>2. Max Attempts<br>3. Custom Delays
%% Monitoring Aspects
Note over Monitor: Monitoring:<br>1. Queue Size<br>2. Processing Rate<br>3. Error Rate<br>4. Worker Health

View file

@ -0,0 +1,98 @@
sequenceDiagram
participant Client
participant Server as HTTP Server
participant Kernel as HTTP Kernel
participant Pipeline as Middleware Pipeline
participant Router
participant Controller
participant Services
participant Events
participant DB as Database
%% Initial Request
Client->>Server: HTTP Request
activate Server
Server->>Kernel: Handle Request
activate Kernel
%% Global Middleware
Kernel->>Pipeline: Process Global Middleware
activate Pipeline
Note over Pipeline: - Check Maintenance Mode<br>- Validate Post Size<br>- Trim Strings<br>- Convert Empty to Null
Pipeline-->>Kernel: Request Processed
deactivate Pipeline
%% Route Matching
Kernel->>Router: Match Route
activate Router
Router-->>Kernel: Route Found
deactivate Router
%% Route Middleware
Kernel->>Pipeline: Process Route Middleware
activate Pipeline
Note over Pipeline: - Authentication<br>- Authorization<br>- Throttling<br>- CSRF Protection
Pipeline-->>Kernel: Request Processed
deactivate Pipeline
%% Controller Action
Kernel->>Controller: Handle Request
activate Controller
%% Service Layer
Controller->>Services: Process Business Logic
activate Services
%% Database Operations
Services->>DB: Query Data
activate DB
DB-->>Services: Data Retrieved
deactivate DB
%% Event Dispatching
Services->>Events: Dispatch Events
activate Events
Note over Events: - Model Events<br>- Custom Events<br>- System Events
Events-->>Services: Events Processed
deactivate Events
Services-->>Controller: Logic Processed
deactivate Services
%% Response Generation
Controller-->>Kernel: Generate Response
deactivate Controller
%% Response Middleware
Kernel->>Pipeline: Process Response Middleware
activate Pipeline
Note over Pipeline: - Session<br>- Cookies<br>- Headers<br>- Response Compression
Pipeline-->>Kernel: Response Processed
deactivate Pipeline
%% Final Response
Kernel-->>Server: Return Response
deactivate Kernel
Server-->>Client: HTTP Response
deactivate Server
%% Style Definitions
style Client fill:#f9f,stroke:#333,stroke-width:2px
style Server fill:#bbf,stroke:#333,stroke-width:2px
style Kernel fill:#bbf,stroke:#333,stroke-width:2px
style Pipeline fill:#bfb,stroke:#333,stroke-width:2px
style Router fill:#bfb,stroke:#333,stroke-width:2px
style Controller fill:#bfb,stroke:#333,stroke-width:2px
style Services fill:#fbb,stroke:#333,stroke-width:2px
style Events fill:#fbb,stroke:#333,stroke-width:2px
style DB fill:#fbb,stroke:#333,stroke-width:2px
%% Notes
Note right of Server: Entry Point
Note right of Kernel: Request Processing
Note right of Pipeline: Middleware Chain
Note right of Router: Route Resolution
Note right of Controller: Business Logic
Note right of Services: Service Layer
Note right of Events: Event System
Note right of DB: Data Layer

303
docs/bus_gap_analysis.md Normal file
View file

@ -0,0 +1,303 @@
# Bus Package Gap Analysis
## Overview
This document analyzes the gaps between our Bus package's actual implementation and Laravel's Bus functionality, identifying areas that need implementation or documentation updates.
> **Related Documentation**
> - See [Bus Package Specification](bus_package_specification.md) for current implementation
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for overall status
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Testing Guide](testing_guide.md) for testing approaches
> - See [Getting Started Guide](getting_started.md) for development setup
## Implementation Gaps
### 1. Missing Laravel Features
```dart
// Documented but not implemented:
// 1. Job Chaining
class ChainedCommand {
// Need to implement:
void onConnection(String connection);
void onQueue(String queue);
void delay(Duration duration);
void middleware(List<PipeContract> middleware);
}
// 2. Rate Limiting
class RateLimitedCommand {
// Need to implement:
void rateLimit(int maxAttempts, Duration duration);
void rateLimitPerUser(int maxAttempts, Duration duration);
void withoutOverlapping();
}
// 3. Error Handling
class FailedCommandHandler {
// Need to implement:
Future<void> failed(Command command, Exception exception);
Future<void> retry(String commandId);
Future<void> forget(String commandId);
Future<void> flush();
}
```
### 2. Existing Features Not Documented
```dart
// Implemented but not documented:
// 1. Command Mapping
class CommandMapper {
/// Maps command types to handlers
final Map<Type, Type> _handlers = {};
/// Registers command handler
void map<TCommand extends Command, THandler extends Handler>(
THandler Function() factory
);
}
// 2. Command Context
class CommandContext {
/// Command metadata
final Map<String, dynamic> _metadata = {};
/// Sets command context
void withContext(String key, dynamic value);
/// Gets command context
T? getContext<T>(String key);
}
// 3. Command Lifecycle
class CommandLifecycle {
/// Command hooks
final List<Function> _beforeHandling = [];
final List<Function> _afterHandling = [];
/// Registers lifecycle hooks
void beforeHandling(Function callback);
void afterHandling(Function callback);
}
```
### 3. Integration Points Not Documented
```dart
// 1. Queue Integration
class QueuedCommand {
/// Queue configuration
String get connection => 'default';
String get queue => 'default';
Duration? get delay => null;
/// Queue callbacks
void onQueue(QueueContract queue);
void onConnection(String connection);
}
// 2. Event Integration
class EventedCommand {
/// Event dispatcher
final EventDispatcherContract _events;
/// Dispatches command events
void dispatchCommandEvent(String event, dynamic data);
void subscribeCommandEvents(EventSubscriber subscriber);
}
// 3. Cache Integration
class CachedCommand {
/// Cache configuration
String get cacheKey => '';
Duration get cacheTTL => Duration(minutes: 60);
/// Cache operations
Future<void> cache();
Future<void> clearCache();
}
```
## Documentation Gaps
### 1. Missing API Documentation
```dart
// Need to document:
/// Maps command types to their handlers.
///
/// Example:
/// ```dart
/// mapper.map<CreateOrder, CreateOrderHandler>(
/// () => CreateOrderHandler(repository)
/// );
/// ```
void map<TCommand extends Command, THandler extends Handler>(
THandler Function() factory
);
/// Sets command execution context.
///
/// Example:
/// ```dart
/// command.withContext('user_id', userId);
/// command.withContext('tenant', tenant);
/// ```
void withContext(String key, dynamic value);
```
### 2. Missing Integration Examples
```dart
// Need examples for:
// 1. Queue Integration
var command = CreateOrder(...)
..onQueue('orders')
..delay(Duration(minutes: 5));
await bus.dispatch(command);
// 2. Event Integration
class OrderCommand extends EventedCommand {
void handle() {
// Handle command
dispatchCommandEvent('order.handled', order);
}
}
// 3. Cache Integration
class GetOrderCommand extends CachedCommand {
@override
String get cacheKey => 'order.$orderId';
Future<Order> handle() async {
return await cache(() => repository.find(orderId));
}
}
```
### 3. Missing Test Coverage
```dart
// Need tests for:
void main() {
group('Command Mapping', () {
test('maps commands to handlers', () {
var mapper = CommandMapper();
mapper.map<CreateOrder, CreateOrderHandler>(
() => CreateOrderHandler(repository)
);
var handler = mapper.resolveHandler(CreateOrder());
expect(handler, isA<CreateOrderHandler>());
});
});
group('Command Context', () {
test('handles command context', () {
var command = TestCommand()
..withContext('key', 'value');
expect(command.getContext('key'), equals('value'));
});
});
}
```
## Implementation Priority
1. **High Priority**
- Command chaining (Laravel compatibility)
- Rate limiting (Laravel compatibility)
- Better error handling
2. **Medium Priority**
- Command mapping
- Command context
- Performance optimizations
3. **Low Priority**
- Additional helper methods
- Extended testing utilities
- Debug/profiling tools
## Next Steps
1. **Implementation Tasks**
- Add command chaining
- Add rate limiting
- Add error handling
- Improve queue integration
2. **Documentation Tasks**
- Document command mapping
- Document command context
- Document command lifecycle
- Add integration examples
3. **Testing Tasks**
- Add command mapping tests
- Add context tests
- Add lifecycle tests
- Add integration tests
## Development Guidelines
### 1. Getting Started
Before implementing bus features:
1. Review [Getting Started Guide](getting_started.md)
2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Follow [Testing Guide](testing_guide.md)
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
5. Review [Bus Package Specification](bus_package_specification.md)
### 2. Implementation Process
For each bus feature:
1. Write tests following [Testing Guide](testing_guide.md)
2. Implement following Laravel patterns
3. Document following [Getting Started Guide](getting_started.md#documentation)
4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md)
### 3. Quality Requirements
All implementations must:
1. Pass all tests (see [Testing Guide](testing_guide.md))
2. Meet Laravel compatibility requirements
3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md))
4. Match specifications in [Bus Package Specification](bus_package_specification.md)
### 4. Integration Considerations
When implementing bus features:
1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md)
2. Ensure Laravel compatibility per [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Use testing approaches from [Testing Guide](testing_guide.md)
4. Follow development setup in [Getting Started Guide](getting_started.md)
### 5. Performance Guidelines
Bus system must:
1. Handle high command throughput
2. Process chains efficiently
3. Support async operations
4. Scale horizontally
5. Meet performance targets in [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#performance-benchmarks)
### 6. Testing Requirements
Bus tests must:
1. Cover all command scenarios
2. Test chaining behavior
3. Verify rate limiting
4. Check error handling
5. Follow patterns in [Testing Guide](testing_guide.md)
### 7. Documentation Requirements
Bus documentation must:
1. Explain command patterns
2. Show chaining examples
3. Cover error handling
4. Include performance tips
5. Follow standards in [Getting Started Guide](getting_started.md#documentation)

View file

@ -0,0 +1,424 @@
# Bus Package Specification
## Overview
The Bus package provides a robust command and event bus implementation that matches Laravel's bus functionality. It integrates with our Queue, Event, and Pipeline packages to provide a complete message bus solution with support for command handling, event dispatching, and middleware processing.
> **Related Documentation**
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for implementation status
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Testing Guide](testing_guide.md) for testing approaches
> - See [Getting Started Guide](getting_started.md) for development setup
> - See [Events Package Specification](events_package_specification.md) for event handling
> - See [Queue Package Specification](queue_package_specification.md) for queue integration
## Core Features
### 1. Command Bus
```dart
/// Core command bus implementation
class CommandBus implements CommandBusContract {
final Container _container;
final QueueContract? _queue;
final PipelineContract _pipeline;
CommandBus(
this._container,
this._pipeline, [
this._queue
]);
/// Dispatches a command
Future<dynamic> dispatch(Command command) async {
if (command is ShouldQueue && _queue != null) {
return await dispatchToQueue(command);
}
return await dispatchNow(command);
}
/// Dispatches a command immediately
Future<dynamic> dispatchNow(Command command) async {
var handler = _resolveHandler(command);
return await _pipeline
.send(command)
.through(_getPipes())
.then((cmd) => handler.handle(cmd));
}
/// Dispatches a command to queue
Future<dynamic> dispatchToQueue(Command command) async {
await _queue!.push(QueuedCommandJob(
command: command,
handler: _resolveHandler(command)
));
}
/// Creates a command batch
PendingBatch batch(List<Command> commands) {
return PendingCommandBatch(this, commands);
}
/// Creates a command chain
PendingChain chain(List<Command> commands) {
return PendingCommandChain(this, commands);
}
/// Resolves command handler
Handler _resolveHandler(Command command) {
var handlerType = command.handler;
return _container.make(handlerType);
}
/// Gets command pipes
List<PipeContract> _getPipes() {
return [
TransactionPipe(),
ValidationPipe(),
AuthorizationPipe()
];
}
}
```
### 2. Event Bus
```dart
/// Core event bus implementation
class EventBus implements EventBusContract {
final Container _container;
final EventDispatcherContract _events;
final QueueContract? _queue;
EventBus(
this._container,
this._events, [
this._queue
]);
/// Dispatches an event
Future<void> dispatch(Event event) async {
if (event is ShouldQueue && _queue != null) {
await dispatchToQueue(event);
} else {
await dispatchNow(event);
}
}
/// Dispatches an event immediately
Future<void> dispatchNow(Event event) async {
await _events.dispatch(event);
}
/// Dispatches an event to queue
Future<void> dispatchToQueue(Event event) async {
await _queue!.push(QueuedEventJob(
event: event,
listeners: _events.getListeners(event.runtimeType)
));
}
/// Registers an event listener
void listen<T>(void Function(T event) listener) {
_events.listen<T>(listener);
}
/// Registers an event subscriber
void subscribe(EventSubscriber subscriber) {
_events.subscribe(subscriber);
}
}
```
### 3. Bus Middleware
```dart
/// Transaction middleware
class TransactionPipe implements PipeContract {
final DatabaseManager _db;
TransactionPipe(this._db);
@override
Future<dynamic> handle(dynamic passable, Function next) async {
return await _db.transaction((tx) async {
return await next(passable);
});
}
}
/// Validation middleware
class ValidationPipe implements PipeContract {
final Validator _validator;
ValidationPipe(this._validator);
@override
Future<dynamic> handle(dynamic passable, Function next) async {
if (passable is ValidatableCommand) {
await _validator.validate(
passable.toMap(),
passable.rules()
);
}
return await next(passable);
}
}
/// Authorization middleware
class AuthorizationPipe implements PipeContract {
final AuthManager _auth;
AuthorizationPipe(this._auth);
@override
Future<dynamic> handle(dynamic passable, Function next) async {
if (passable is AuthorizableCommand) {
if (!await passable.authorize(_auth)) {
throw UnauthorizedException();
}
}
return await next(passable);
}
}
```
### 4. Command Batching
```dart
/// Pending command batch
class PendingCommandBatch implements PendingBatch {
final CommandBus _bus;
final List<Command> _commands;
bool _allowFailures = false;
PendingCommandBatch(this._bus, this._commands);
/// Allows failures in batch
PendingBatch allowFailures() {
_allowFailures = true;
return this;
}
/// Dispatches the batch
Future<void> dispatch() async {
for (var command in _commands) {
try {
await _bus.dispatchNow(command);
} catch (e) {
if (!_allowFailures) rethrow;
}
}
}
}
/// Pending command chain
class PendingCommandChain implements PendingChain {
final CommandBus _bus;
final List<Command> _commands;
PendingCommandChain(this._bus, this._commands);
/// Dispatches the chain
Future<void> dispatch() async {
dynamic result;
for (var command in _commands) {
if (command is ChainedCommand) {
command.setPreviousResult(result);
}
result = await _bus.dispatchNow(command);
}
}
}
```
## Integration Examples
### 1. Command Bus Usage
```dart
// Define command
class CreateOrder implements Command {
final String customerId;
final List<String> products;
@override
Type get handler => CreateOrderHandler;
}
// Define handler
class CreateOrderHandler implements Handler<CreateOrder> {
final OrderRepository _orders;
CreateOrderHandler(this._orders);
@override
Future<Order> handle(CreateOrder command) async {
return await _orders.create(
customerId: command.customerId,
products: command.products
);
}
}
// Dispatch command
var order = await bus.dispatch(CreateOrder(
customerId: '123',
products: ['abc', 'xyz']
));
```
### 2. Event Bus Usage
```dart
// Define event
class OrderCreated implements Event {
final Order order;
OrderCreated(this.order);
}
// Register listener
eventBus.listen<OrderCreated>((event) async {
await notifyCustomer(event.order);
});
// Dispatch event
await eventBus.dispatch(OrderCreated(order));
```
### 3. Command Batching
```dart
// Create batch
await bus.batch([
CreateOrder(...),
UpdateInventory(...),
NotifyShipping(...)
])
.allowFailures()
.dispatch();
// Create chain
await bus.chain([
CreateOrder(...),
ProcessPayment(...),
ShipOrder(...)
])
.dispatch();
```
## Testing
```dart
void main() {
group('Command Bus', () {
test('dispatches commands', () async {
var bus = CommandBus(container, pipeline);
var command = CreateOrder(...);
await bus.dispatch(command);
verify(() => handler.handle(command)).called(1);
});
test('handles command batch', () async {
var bus = CommandBus(container, pipeline);
await bus.batch([
CreateOrder(...),
UpdateInventory(...)
]).dispatch();
verify(() => bus.dispatchNow(any())).called(2);
});
});
group('Event Bus', () {
test('dispatches events', () async {
var bus = EventBus(container, dispatcher);
var event = OrderCreated(order);
await bus.dispatch(event);
verify(() => dispatcher.dispatch(event)).called(1);
});
test('queues events', () async {
var bus = EventBus(container, dispatcher, queue);
var event = OrderShipped(order);
await bus.dispatch(event);
verify(() => queue.push(any())).called(1);
});
});
}
```
## Next Steps
1. Implement core bus features
2. Add middleware support
3. Add batching/chaining
4. Add queue integration
5. Write tests
6. Add benchmarks
## Development Guidelines
### 1. Getting Started
Before implementing bus features:
1. Review [Getting Started Guide](getting_started.md)
2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Follow [Testing Guide](testing_guide.md)
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
5. Review [Events Package Specification](events_package_specification.md)
6. Review [Queue Package Specification](queue_package_specification.md)
### 2. Implementation Process
For each bus feature:
1. Write tests following [Testing Guide](testing_guide.md)
2. Implement following Laravel patterns
3. Document following [Getting Started Guide](getting_started.md#documentation)
4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md)
### 3. Quality Requirements
All implementations must:
1. Pass all tests (see [Testing Guide](testing_guide.md))
2. Meet Laravel compatibility requirements
3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md))
4. Match specifications in related packages
### 4. Integration Considerations
When implementing bus features:
1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md)
2. Ensure Laravel compatibility per [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Use testing approaches from [Testing Guide](testing_guide.md)
4. Follow development setup in [Getting Started Guide](getting_started.md)
### 5. Performance Guidelines
Bus system must:
1. Handle high command throughput
2. Process events efficiently
3. Support async operations
4. Scale horizontally
5. Meet performance targets in [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#performance-benchmarks)
### 6. Testing Requirements
Bus tests must:
1. Cover all command scenarios
2. Test event handling
3. Verify queue integration
4. Check middleware behavior
5. Follow patterns in [Testing Guide](testing_guide.md)
### 7. Documentation Requirements
Bus documentation must:
1. Explain command patterns
2. Show event examples
3. Cover error handling
4. Include performance tips
5. Follow standards in [Getting Started Guide](getting_started.md#documentation)

335
docs/config_gap_analysis.md Normal file
View file

@ -0,0 +1,335 @@
# Config Package Gap Analysis
## Overview
This document analyzes the gaps between our current configuration handling (in Core package) and Laravel's Config package functionality, identifying what needs to be implemented as a standalone Config package.
> **Related Documentation**
> - See [Config Package Specification](config_package_specification.md) for current implementation
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for overall status
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Testing Guide](testing_guide.md) for testing approaches
> - See [Getting Started Guide](getting_started.md) for development setup
## Implementation Gaps
### 1. Missing Package Structure
```dart
// Need to create dedicated Config package:
packages/config/
├── lib/
│ ├── src/
│ │ ├── config_repository.dart
│ │ ├── environment_loader.dart
│ │ ├── config_loader.dart
│ │ └── config_cache.dart
│ └── config.dart
├── test/
└── example/
```
### 2. Missing Core Features
```dart
// 1. Config Repository
class ConfigRepository {
// Need to implement:
T? get<T>(String key, [T? defaultValue]);
void set(String key, dynamic value);
bool has(String key);
Map<String, dynamic> all();
void merge(Map<String, dynamic> items);
}
// 2. Environment Loading
class EnvironmentLoader {
// Need to implement:
Future<void> load([String? path]);
String? get(String key, [String? defaultValue]);
void set(String key, String value);
bool has(String key);
}
// 3. Configuration Loading
class ConfigurationLoader {
// Need to implement:
Future<Map<String, dynamic>> load();
Future<Map<String, dynamic>> loadFile(String path);
Future<void> reload();
}
```
### 3. Missing Laravel Features
```dart
// 1. Package Configuration
class PackageConfig {
// Need to implement:
Future<void> publish(String package, Map<String, String> paths);
Future<void> publishForce(String package, Map<String, String> paths);
List<String> publishedPackages();
}
// 2. Configuration Groups
class ConfigurationGroups {
// Need to implement:
void group(String name, List<String> paths);
List<String> getGroup(String name);
bool hasGroup(String name);
}
// 3. Configuration Caching
class ConfigCache {
// Need to implement:
Future<void> cache(Map<String, dynamic> config);
Future<Map<String, dynamic>?> load();
Future<void> clear();
}
```
## Integration Gaps
### 1. Container Integration
```dart
// Need to implement:
class ConfigServiceProvider {
void register() {
// Register config repository
container.singleton<ConfigContract>((c) =>
ConfigRepository(
loader: c.make<ConfigurationLoader>(),
cache: c.make<ConfigCache>()
)
);
// Register environment loader
container.singleton<EnvironmentLoader>((c) =>
EnvironmentLoader(
path: c.make<PathResolver>().base
)
);
}
}
```
### 2. Package Integration
```dart
// Need to implement:
class PackageServiceProvider {
void register() {
// Register package config
publishConfig('my-package', {
'config/my-package.php': 'my-package'
});
}
void boot() {
// Merge package config
config.merge({
'my-package': {
'key': 'value'
}
});
}
}
```
### 3. Environment Integration
```dart
// Need to implement:
class EnvironmentServiceProvider {
void boot() {
var env = container.make<EnvironmentLoader>();
// Load environment files
env.load();
if (env.get('APP_ENV') == 'local') {
env.load('.env.local');
}
// Set environment variables
config.set('app.env', env.get('APP_ENV', 'production'));
config.set('app.debug', env.get('APP_DEBUG', 'false') == 'true');
}
}
```
## Documentation Gaps
### 1. Missing API Documentation
```dart
// Need to document:
/// Manages application configuration.
///
/// Provides access to configuration values using dot notation:
/// ```dart
/// var dbHost = config.get<String>('database.connections.mysql.host');
/// ```
class ConfigRepository {
/// Gets a configuration value.
///
/// Returns [defaultValue] if key not found.
T? get<T>(String key, [T? defaultValue]);
}
```
### 2. Missing Usage Examples
```dart
// Need examples for:
// 1. Basic Configuration
var appName = config.get<String>('app.name', 'My App');
var debug = config.get<bool>('app.debug', false);
// 2. Environment Configuration
var dbConfig = {
'host': env('DB_HOST', 'localhost'),
'port': env('DB_PORT', '3306'),
'database': env('DB_DATABASE'),
'username': env('DB_USERNAME'),
'password': env('DB_PASSWORD')
};
// 3. Package Configuration
class MyPackageServiceProvider {
void register() {
publishConfig('my-package', {
'config/my-package.php': 'my-package'
});
}
}
```
### 3. Missing Test Coverage
```dart
// Need tests for:
void main() {
group('Config Repository', () {
test('gets nested values', () {
var config = ConfigRepository({
'app': {
'name': 'Test App',
'nested': {'key': 'value'}
}
});
expect(config.get('app.name'), equals('Test App'));
expect(config.get('app.nested.key'), equals('value'));
});
});
group('Environment Loader', () {
test('loads env files', () async {
var env = EnvironmentLoader();
await env.load('.env.test');
expect(env.get('APP_NAME'), equals('Test App'));
expect(env.get('APP_ENV'), equals('testing'));
});
});
}
```
## Implementation Priority
1. **High Priority**
- Create Config package structure
- Implement core repository
- Add environment loading
- Add configuration loading
2. **Medium Priority**
- Add package configuration
- Add configuration groups
- Add configuration caching
- Add container integration
3. **Low Priority**
- Add helper functions
- Add testing utilities
- Add debugging tools
## Next Steps
1. **Package Creation**
- Create package structure
- Move config code from Core
- Add package dependencies
- Setup testing
2. **Core Implementation**
- Implement ConfigRepository
- Implement EnvironmentLoader
- Implement ConfigurationLoader
- Add caching support
3. **Integration Implementation**
- Add container integration
- Add package support
- Add environment support
- Add service providers
Would you like me to:
1. Create the Config package structure?
2. Start implementing core features?
3. Create detailed implementation plans?
## Development Guidelines
### 1. Getting Started
Before implementing config features:
1. Review [Getting Started Guide](getting_started.md)
2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Follow [Testing Guide](testing_guide.md)
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
5. Review [Config Package Specification](config_package_specification.md)
### 2. Implementation Process
For each config feature:
1. Write tests following [Testing Guide](testing_guide.md)
2. Implement following Laravel patterns
3. Document following [Getting Started Guide](getting_started.md#documentation)
4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md)
### 3. Quality Requirements
All implementations must:
1. Pass all tests (see [Testing Guide](testing_guide.md))
2. Meet Laravel compatibility requirements
3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md))
4. Match specifications in [Config Package Specification](config_package_specification.md)
### 4. Integration Considerations
When implementing config features:
1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md)
2. Ensure Laravel compatibility per [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Use testing approaches from [Testing Guide](testing_guide.md)
4. Follow development setup in [Getting Started Guide](getting_started.md)
### 5. Performance Guidelines
Config system must:
1. Cache configuration efficiently
2. Minimize file I/O
3. Support lazy loading
4. Handle environment variables efficiently
5. Meet performance targets in [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#performance-benchmarks)
### 6. Testing Requirements
Config tests must:
1. Cover all configuration scenarios
2. Test environment handling
3. Verify caching behavior
4. Check file operations
5. Follow patterns in [Testing Guide](testing_guide.md)
### 7. Documentation Requirements
Config documentation must:
1. Explain configuration patterns
2. Show environment examples
3. Cover caching strategies
4. Include performance tips
5. Follow standards in [Getting Started Guide](getting_started.md#documentation)

View file

@ -0,0 +1,451 @@
# Config Package Specification
## Overview
The Config package provides a flexible configuration management system that matches Laravel's config functionality. It integrates with our Container and Package systems while supporting hierarchical configuration, environment-based settings, and caching.
> **Related Documentation**
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for implementation status
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Testing Guide](testing_guide.md) for testing approaches
> - See [Getting Started Guide](getting_started.md) for development setup
> - See [Contracts Package Specification](contracts_package_specification.md) for config contracts
## Core Features
### 1. Configuration Repository
```dart
/// Core configuration repository
class ConfigRepository implements ConfigContract {
final Container _container;
final Map<String, dynamic> _items;
final EnvironmentLoader _env;
final ConfigCache? _cache;
ConfigRepository(
this._container, [
Map<String, dynamic>? items,
EnvironmentLoader? env,
ConfigCache? cache
]) : _items = items ?? {},
_env = env ?? EnvironmentLoader(),
_cache = cache;
@override
T? get<T>(String key, [T? defaultValue]) {
var value = _getNestedValue(key);
if (value == null) {
return defaultValue;
}
return _cast<T>(value);
}
@override
void set(String key, dynamic value) {
_setNestedValue(key, value);
_cache?.clear();
}
@override
bool has(String key) {
return _getNestedValue(key) != null;
}
/// Gets all configuration items
Map<String, dynamic> all() => Map.from(_items);
/// Merges configuration values
void merge(Map<String, dynamic> items) {
_items.addAll(_deepMerge(_items, items));
_cache?.clear();
}
/// Deep merges two maps
Map<String, dynamic> _deepMerge(
Map<String, dynamic> target,
Map<String, dynamic> source
) {
source.forEach((key, value) {
if (value is Map && target[key] is Map) {
target[key] = _deepMerge(
target[key] as Map<String, dynamic>,
value as Map<String, dynamic>
);
} else {
target[key] = value;
}
});
return target;
}
/// Casts a value to the requested type
T _cast<T>(dynamic value) {
if (value is T) return value;
// Handle common type conversions
if (T == bool) {
if (value is String) {
return (value.toLowerCase() == 'true') as T;
}
return (value == 1) as T;
}
if (T == int && value is String) {
return int.parse(value) as T;
}
if (T == double && value is String) {
return double.parse(value) as T;
}
throw ConfigCastException(
'Cannot cast $value to $T'
);
}
}
```
### 2. Environment Management
```dart
/// Manages environment configuration
class EnvironmentManager {
final Container _container;
final Map<String, String> _cache = {};
final List<String> _files = ['.env'];
EnvironmentManager(this._container);
/// Loads environment files
Future<void> load([String? path]) async {
path ??= _container.make<PathResolver>().base;
for (var file in _files) {
var envFile = File('$path/$file');
if (await envFile.exists()) {
var contents = await envFile.readAsString();
_parseEnvFile(contents);
}
}
}
/// Gets an environment variable
String? get(String key, [String? defaultValue]) {
return _cache[key] ??
Platform.environment[key] ??
defaultValue;
}
/// Sets an environment variable
void set(String key, String value) {
_cache[key] = value;
}
/// Adds an environment file
void addEnvFile(String file) {
_files.add(file);
}
/// Parses an environment file
void _parseEnvFile(String contents) {
var lines = contents.split('\n');
for (var line in lines) {
if (_isComment(line)) continue;
if (_isEmpty(line)) continue;
var parts = line.split('=');
if (parts.length != 2) continue;
var key = parts[0].trim();
var value = _parseValue(parts[1].trim());
_cache[key] = value;
}
}
/// Parses an environment value
String _parseValue(String value) {
// Remove quotes
if (value.startsWith('"') && value.endsWith('"')) {
value = value.substring(1, value.length - 1);
}
// Handle special values
switch (value.toLowerCase()) {
case 'true':
case '(true)':
return 'true';
case 'false':
case '(false)':
return 'false';
case 'empty':
case '(empty)':
return '';
case 'null':
case '(null)':
return '';
}
return value;
}
}
```
### 3. Package Configuration
```dart
/// Manages package configuration publishing
class ConfigPublisher {
final Container _container;
final Map<String, List<String>> _publishGroups = {};
ConfigPublisher(this._container);
/// Publishes configuration files
Future<void> publish(
String package,
Map<String, String> paths, [
List<String>? groups
]) async {
var resolver = _container.make<PathResolver>();
var configPath = resolver.config;
for (var entry in paths.entries) {
var source = entry.key;
var dest = '$configPath/${entry.value}';
await _publishFile(source, dest);
if (groups != null) {
for (var group in groups) {
_publishGroups.putIfAbsent(group, () => [])
.add(dest);
}
}
}
}
/// Gets files in a publish group
List<String> getGroup(String name) {
return _publishGroups[name] ?? [];
}
/// Copies a configuration file
Future<void> _publishFile(
String source,
String destination
) async {
var sourceFile = File(source);
var destFile = File(destination);
if (!await destFile.exists()) {
await destFile.create(recursive: true);
await sourceFile.copy(destination);
}
}
}
```
### 4. Configuration Cache
```dart
/// Caches configuration values
class ConfigCache {
final Container _container;
final String _cacheKey = 'config.cache';
ConfigCache(this._container);
/// Caches configuration values
Future<void> cache(Map<String, dynamic> items) async {
var cache = _container.make<CacheContract>();
await cache.forever(_cacheKey, items);
}
/// Gets cached configuration
Future<Map<String, dynamic>?> get() async {
var cache = _container.make<CacheContract>();
return await cache.get<Map<String, dynamic>>(_cacheKey);
}
/// Clears cached configuration
Future<void> clear() async {
var cache = _container.make<CacheContract>();
await cache.forget(_cacheKey);
}
}
```
## Integration Examples
### 1. Package Configuration
```dart
class MyPackageServiceProvider extends ServiceProvider {
@override
void register() {
// Publish package config
publishConfig('my-package', {
'config/my-package.php': 'my-package'
});
}
@override
void boot() {
// Merge package config
var config = container.make<ConfigContract>();
config.merge({
'my-package': {
'key': 'value'
}
});
}
}
```
### 2. Environment Configuration
```dart
class AppServiceProvider extends ServiceProvider {
@override
void boot() {
var env = container.make<EnvironmentManager>();
// Add environment files
env.addEnvFile('.env.local');
if (protevusEnv.isTesting) {
env.addEnvFile('.env.testing');
}
// Load environment
await env.load();
}
}
```
### 3. Configuration Cache
```dart
class CacheCommand {
Future<void> handle() async {
var config = container.make<ConfigContract>();
var cache = container.make<ConfigCache>();
// Cache config
await cache.cache(config.all());
// Clear config cache
await cache.clear();
}
}
```
## Testing
```dart
void main() {
group('Config Repository', () {
test('merges configuration', () {
var config = ConfigRepository(container);
config.set('database', {
'default': 'mysql',
'connections': {
'mysql': {'host': 'localhost'}
}
});
config.merge({
'database': {
'connections': {
'mysql': {'port': 3306}
}
}
});
expect(
config.get('database.connections.mysql'),
equals({
'host': 'localhost',
'port': 3306
})
);
});
});
group('Environment Manager', () {
test('loads multiple env files', () async {
var env = EnvironmentManager(container);
env.addEnvFile('.env.testing');
await env.load();
expect(env.get('APP_ENV'), equals('testing'));
});
});
}
```
## Next Steps
1. Complete package config publishing
2. Add config merging
3. Enhance environment handling
4. Add caching improvements
5. Write more tests
Would you like me to enhance any other package specifications?
## Development Guidelines
### 1. Getting Started
Before implementing config features:
1. Review [Getting Started Guide](getting_started.md)
2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Follow [Testing Guide](testing_guide.md)
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
5. Understand [Contracts Package Specification](contracts_package_specification.md)
### 2. Implementation Process
For each config feature:
1. Write tests following [Testing Guide](testing_guide.md)
2. Implement following Laravel patterns
3. Document following [Getting Started Guide](getting_started.md#documentation)
4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md)
### 3. Quality Requirements
All implementations must:
1. Pass all tests (see [Testing Guide](testing_guide.md))
2. Meet Laravel compatibility requirements
3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md))
4. Implement required contracts (see [Contracts Package Specification](contracts_package_specification.md))
### 4. Integration Considerations
When implementing config features:
1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md)
2. Ensure Laravel compatibility per [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Use testing approaches from [Testing Guide](testing_guide.md)
4. Follow development setup in [Getting Started Guide](getting_started.md)
5. Implement all contracts from [Contracts Package Specification](contracts_package_specification.md)
### 5. Performance Guidelines
Config system must:
1. Cache configuration efficiently
2. Minimize file I/O
3. Support lazy loading
4. Handle environment variables efficiently
5. Meet performance targets in [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#performance-benchmarks)
### 6. Testing Requirements
Config tests must:
1. Cover all configuration scenarios
2. Test environment handling
3. Verify caching behavior
4. Check file operations
5. Follow patterns in [Testing Guide](testing_guide.md)
### 7. Documentation Requirements
Config documentation must:
1. Explain configuration patterns
2. Show environment examples
3. Cover caching strategies
4. Include performance tips
5. Follow standards in [Getting Started Guide](getting_started.md#documentation)

View file

@ -0,0 +1,379 @@
# Container Feature Integration Guide
## Overview
This guide demonstrates how the Container's three major features work together to provide powerful dependency management:
1. Contextual Binding - Different implementations based on context
2. Method Injection - Automatic dependency resolution for methods
3. Tagged Bindings - Grouping related services
## Real World Example: Multi-tenant Reporting System
Let's build a complete multi-tenant reporting system that showcases all three features working together.
### System Requirements
1. Multiple tenants (clients) each need their own:
- Database connection
- Storage system
- Report formatting
2. Various types of reports:
- Performance reports
- Financial reports
- User activity reports
3. Each report needs:
- Data access
- Formatting
- Storage
- Logging
### Base Interfaces
```dart
/// Base interface for all reports
abstract class Report {
Future<void> generate();
Future<void> save();
}
/// Database connection interface
abstract class Database {
Future<List<Map<String, dynamic>>> query(String sql);
}
/// Storage system interface
abstract class Storage {
Future<void> save(String path, List<int> data);
Future<List<int>> load(String path);
}
/// Report formatter interface
abstract class ReportFormatter {
String format(Map<String, dynamic> data);
}
```
### Tenant-Specific Implementations
```dart
/// Tenant A's database implementation
class TenantADatabase implements Database {
@override
Future<List<Map<String, dynamic>>> query(String sql) {
// Tenant A specific database logic
}
}
/// Tenant B's database implementation
class TenantBDatabase implements Database {
@override
Future<List<Map<String, dynamic>>> query(String sql) {
// Tenant B specific database logic
}
}
/// Similar implementations for Storage and Formatter...
```
### Report Implementations
```dart
class PerformanceReport implements Report {
final Database db;
final Storage storage;
final ReportFormatter formatter;
PerformanceReport(this.db, this.storage, this.formatter);
@override
Future<void> generate() async {
var data = await db.query('SELECT * FROM performance_metrics');
var formatted = formatter.format(data);
await storage.save('performance.report', formatted.codeUnits);
}
}
// Similar implementations for Financial and UserActivity reports...
```
### Using All Three Features Together
1. First, set up contextual bindings for tenant-specific services:
```dart
void configureTenantA(Container container) {
// Bind tenant-specific implementations
container.when(TenantAContext)
.needs<Database>()
.give(TenantADatabase());
container.when(TenantAContext)
.needs<Storage>()
.give(TenantAStorage());
container.when(TenantAContext)
.needs<ReportFormatter>()
.give(TenantAFormatter());
}
void configureTenantB(Container container) {
// Similar bindings for Tenant B...
}
```
2. Set up tagged bindings for reports:
```dart
void configureReports(Container container) {
// Bind report implementations
container.bind<PerformanceReport>(PerformanceReport);
container.bind<FinancialReport>(FinancialReport);
container.bind<UserActivityReport>(UserActivityReport);
// Tag them for easy retrieval
container.tag([
PerformanceReport,
FinancialReport,
UserActivityReport
], 'reports');
// Additional tags for categorization
container.tag([PerformanceReport], 'metrics-reports');
container.tag([FinancialReport], 'financial-reports');
}
```
3. Create a report manager that uses method injection:
```dart
class ReportManager {
final Container container;
ReportManager(this.container);
/// Generates all reports for a tenant
/// Uses method injection for the logger parameter
Future<void> generateAllReports(
TenantContext tenant,
{required DateTime date}
) async {
// Get all tagged reports
var reports = container.taggedAs<Report>('reports');
// Generate each report using tenant context
for (var report in reports) {
await container.call(
report,
'generate',
parameters: {'date': date},
context: tenant // Uses contextual binding
);
}
}
/// Generates specific report types
/// Uses method injection for dependencies
Future<void> generateMetricsReports(
TenantContext tenant,
Logger logger, // Injected automatically
MetricsService metrics // Injected automatically
) async {
var reports = container.taggedAs<Report>('metrics-reports');
for (var report in reports) {
logger.info('Generating metrics report: ${report.runtimeType}');
await container.call(report, 'generate', context: tenant);
metrics.recordReportGeneration(report);
}
}
}
```
### Using the Integrated System
```dart
void main() async {
var container = Container();
// Configure container
configureTenantA(container);
configureTenantB(container);
configureReports(container);
// Create report manager
var manager = ReportManager(container);
// Generate reports for Tenant A
await manager.generateAllReports(
TenantAContext(),
date: DateTime.now()
);
// Generate only metrics reports for Tenant B
await manager.generateMetricsReports(
TenantBContext()
);
}
```
## How the Features Work Together
1. **Contextual Binding** ensures:
- Each tenant gets their own implementations
- Services are properly scoped
- No cross-tenant data leakage
2. **Method Injection** provides:
- Automatic dependency resolution
- Clean method signatures
- Flexible parameter handling
3. **Tagged Bindings** enable:
- Easy service grouping
- Dynamic service discovery
- Flexible categorization
## Common Integration Patterns
1. **Service Location with Context**
```dart
// Get tenant-specific service
var db = container.make<Database>(context: tenantContext);
// Get all services of a type for a tenant
var reports = container.taggedAs<Report>('reports')
.map((r) => container.make(r, context: tenantContext))
.toList();
```
2. **Method Injection with Tags**
```dart
Future<void> processReports(Logger logger) async {
// Logger is injected, reports are retrieved by tag
var reports = container.taggedAs<Report>('reports');
for (var report in reports) {
logger.info('Processing ${report.runtimeType}');
await container.call(report, 'process');
}
}
```
3. **Contextual Services with Tags**
```dart
Future<void> generateTenantReports(TenantContext tenant) async {
// Get all reports
var reports = container.taggedAs<Report>('reports');
// Process each with tenant context
for (var report in reports) {
await container.call(
report,
'generate',
context: tenant
);
}
}
```
## Best Practices
1. **Clear Service Organization**
```dart
// Group related tags
container.tag([Service1, Service2], 'data-services');
container.tag([Service1], 'cacheable-services');
// Group related contexts
container.when(TenantContext)
.needs<Database>()
.give(TenantDatabase());
```
2. **Consistent Dependency Resolution**
```dart
// Prefer method injection for flexible dependencies
Future<void> processReport(
Report report,
Logger logger, // Injected
MetricsService metrics // Injected
) async {
// Implementation
}
// Use contextual binding for tenant-specific services
container.when(TenantContext)
.needs<Storage>()
.give(TenantStorage());
```
3. **Documentation**
```dart
/// Report processor that handles multiple report types
///
/// Uses the following container features:
/// - Tagged bindings for report retrieval ('reports' tag)
/// - Contextual binding for tenant-specific services
/// - Method injection for logging and metrics
class ReportProcessor {
// Implementation
}
```
## Testing Integrated Features
```dart
void main() {
group('Integrated Container Features', () {
late Container container;
setUp(() {
container = Container();
// Set up test bindings
configureTenantA(container);
configureReports(container);
});
test('should handle tenant-specific tagged services', () {
var tenantA = TenantAContext();
// Get all reports for tenant
var reports = container.taggedAs<Report>('reports')
.map((r) => container.make(r, context: tenantA))
.toList();
expect(reports, hasLength(3));
expect(reports.every((r) => r.db is TenantADatabase), isTrue);
});
test('should inject dependencies with context', () async {
var processor = ReportProcessor();
var tenantA = TenantAContext();
await container.call(
processor,
'processReports',
context: tenantA
);
// Verify correct services were injected
verify(() => processor.logger is Logger).called(1);
verify(() => processor.db is TenantADatabase).called(1);
});
});
}
```
## Next Steps
1. Implement integration tests
2. Add performance monitoring
3. Add dependency validation
4. Create usage documentation
5. Add debugging tools
6. Create migration guides
Would you like me to create detailed specifications for any of these next steps?

View file

@ -0,0 +1,261 @@
# Container Package Gap Analysis
## Overview
This document analyzes the gaps between our Container package's actual implementation and our documentation, identifying areas that need implementation or documentation updates. It also outlines the migration strategy to achieve full Laravel compatibility.
> **Related Documentation**
> - See [Container Package Specification](container_package_specification.md) for current implementation
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for overall status
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Testing Guide](testing_guide.md) for testing requirements
## Implementation Status
> **Status Note**: This status aligns with our [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#implementation-status). See there for overall framework status.
### 1. Core Features
#### Implemented
```dart
✓ Basic dependency injection
✓ Service location
✓ Auto-wiring
✓ Parent/child containers
✓ Named singletons
✓ Lazy singleton registration
✓ Async dependency resolution
```
#### Partially Implemented
```dart
~ Contextual binding (basic structure)
~ Method injection (basic reflection)
~ Tagged bindings (basic tagging)
```
#### Not Implemented
```dart
- Advanced contextual binding features
* Instance-based context
* Multiple contexts
* Context inheritance
- Advanced method injection features
* Parameter validation
* Optional parameters
* Named parameters
- Advanced tagged binding features
* Tag inheritance
* Tag groups
* Tag conditions
```
## Migration Strategy
> **Integration Note**: This migration strategy follows patterns from [Foundation Integration Guide](foundation_integration_guide.md). See there for detailed integration examples.
### Phase 1: Internal Restructuring (No Breaking Changes)
1. **Extract Binding Logic**
```dart
// Move from current implementation:
class Container {
final Map<Type, Object> _bindings = {};
void bind<T>(T instance) => _bindings[T] = instance;
}
// To new implementation:
class Container {
final Map<Type, Binding> _bindings = {};
void bind<T>(T Function(Container) concrete) {
_bindings[T] = Binding(
concrete: concrete,
shared: false,
implementedType: T
);
}
}
```
2. **Add Resolution Context**
```dart
class Container {
T make<T>([dynamic context]) {
var resolutionContext = ResolutionContext(
resolvingType: T,
context: context,
container: this,
resolutionStack: {}
);
return _resolve(T, resolutionContext);
}
}
```
### Phase 2: Add New Features (Backward Compatible)
1. **Contextual Binding**
```dart
class Container {
final Map<Type, Map<Type, Binding>> _contextualBindings = {};
ContextualBindingBuilder when(Type concrete) {
return ContextualBindingBuilder(this, concrete);
}
}
```
2. **Method Injection**
```dart
class Container {
dynamic call(
Object instance,
String methodName, [
Map<String, dynamic>? parameters
]) {
var method = reflector.reflectInstance(instance)
.type
.declarations[Symbol(methodName)];
var resolvedParams = _resolveMethodParameters(
method,
parameters
);
return Function.apply(
instance.runtimeType.getMethod(methodName),
resolvedParams
);
}
}
```
3. **Tagged Bindings**
```dart
class Container {
final Map<String, Set<Type>> _tags = {};
void tag(List<Type> types, String tag) {
_tags.putIfAbsent(tag, () => {}).addAll(types);
}
List<T> taggedAs<T>(String tag) {
return _tags[tag]?.map((t) => make<T>(t)).toList() ?? [];
}
}
```
### Phase 3: Performance Optimization
> **Performance Note**: These optimizations align with our [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#performance-benchmarks) performance targets.
1. **Add Resolution Cache**
```dart
class Container {
final ResolutionCache _cache = ResolutionCache();
T make<T>([dynamic context]) {
var cached = _cache.get<T>(context);
if (cached != null) return cached;
var instance = _resolve<T>(T, context);
_cache.cache(instance, context);
return instance;
}
}
```
2. **Add Reflection Cache**
```dart
class Container {
final ReflectionCache _reflectionCache = ReflectionCache();
dynamic call(Object instance, String methodName, [Map<String, dynamic>? parameters]) {
var methodMirror = _reflectionCache.getMethod(
instance.runtimeType,
methodName
) ?? _cacheMethod(instance, methodName);
return _invokeMethod(instance, methodMirror, parameters);
}
}
```
## Backward Compatibility Requirements
> **Note**: These requirements ensure compatibility while following [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) guidelines.
1. **Maintain Existing APIs**
```dart
// These must continue to work:
container.make<T>();
container.makeAsync<T>();
container.has<T>();
container.hasNamed();
container.registerFactory<T>();
container.registerSingleton<T>();
container.registerNamedSingleton<T>();
container.registerLazySingleton<T>();
```
2. **Preserve Behavior**
```dart
// Parent/child resolution
var parent = Container(reflector);
var child = parent.createChild();
parent.registerSingleton<Service>(service);
var resolved = child.make<Service>(); // Must work
// Named singletons
container.registerNamedSingleton('key', service);
var found = container.findByName('key'); // Must work
// Async resolution
var future = container.makeAsync<Service>(); // Must work
```
## Implementation Priority
> **Priority Note**: These priorities align with our [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#implementation-priorities).
### 1. High Priority
- Complete contextual binding implementation
- Add parameter validation to method injection
- Implement tag inheritance
### 2. Medium Priority
- Add cache integration
- Improve error handling
- Add event system integration
### 3. Low Priority
- Add helper methods
- Add debugging tools
- Add performance monitoring
## Development Guidelines
### 1. Getting Started
Before implementing features:
1. Review [Getting Started Guide](getting_started.md)
2. Check [Container Package Specification](container_package_specification.md)
3. Follow [Testing Guide](testing_guide.md)
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
### 2. Implementation Process
For each feature:
1. Write tests following [Testing Guide](testing_guide.md)
2. Implement following [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Document following [Getting Started Guide](getting_started.md#documentation)
4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md)
## Next Steps
Would you like me to:
1. Start implementing Phase 1 changes?
2. Create detailed specifications for Phase 2?
3. Design the caching system for Phase 3?

View file

@ -0,0 +1,452 @@
# Container Migration Guide
## Overview
This guide helps you migrate from the current Container implementation to the new Laravel-compatible version. It covers:
1. Breaking changes
2. New features
3. Migration strategies
4. Code examples
5. Best practices
## Breaking Changes
### 1. Binding Registration
#### Old Way
```dart
// Old implementation
container.bind<Service>(instance);
container.singleton<Service>(instance);
```
#### New Way
```dart
// New implementation
container.bind<Service>((c) => instance);
container.singleton<Service>((c) => instance);
// With contextual binding
container.when(UserController)
.needs<Service>()
.give((c) => SpecialService());
```
### 2. Service Resolution
#### Old Way
```dart
// Old implementation
var service = container.make<Service>();
var namedService = container.makeNamed<Service>('name');
```
#### New Way
```dart
// New implementation
var service = container.make<Service>();
var contextualService = container.make<Service>(context: UserController);
var taggedServices = container.taggedAs<Service>('tag');
```
### 3. Method Injection
#### Old Way
```dart
// Old implementation - manual parameter resolution
class UserService {
void process(User user) {
var logger = container.make<Logger>();
var validator = container.make<Validator>();
// Process user...
}
}
```
#### New Way
```dart
// New implementation - automatic method injection
class UserService {
void process(
User user,
Logger logger, // Automatically injected
Validator validator // Automatically injected
) {
// Process user...
}
}
// Usage
container.call(userService, 'process', {'user': user});
```
## New Features
### 1. Contextual Binding
```dart
// Register different implementations based on context
void setupBindings(Container container) {
// Default storage
container.bind<Storage>((c) => LocalStorage());
// User uploads use cloud storage
container.when(UserUploadController)
.needs<Storage>()
.give((c) => CloudStorage());
// System files use local storage
container.when(SystemFileController)
.needs<Storage>()
.give((c) => LocalStorage());
}
```
### 2. Tagged Bindings
```dart
// Register and tag related services
void setupReportServices(Container container) {
// Register services
container.bind<PerformanceReport>((c) => PerformanceReport());
container.bind<FinancialReport>((c) => FinancialReport());
container.bind<UserReport>((c) => UserReport());
// Tag them for easy retrieval
container.tag([
PerformanceReport,
FinancialReport,
UserReport
], 'reports');
// Additional categorization
container.tag([PerformanceReport], 'metrics');
container.tag([FinancialReport], 'financial');
}
// Usage
var reports = container.taggedAs<Report>('reports');
var metricReports = container.taggedAs<Report>('metrics');
```
### 3. Method Injection
```dart
class ReportGenerator {
void generateReport(
Report report,
Logger logger, // Automatically injected
Formatter formatter, // Automatically injected
{required DateTime date} // Manually provided
) {
logger.info('Generating report...');
var data = report.getData();
var formatted = formatter.format(data);
// Generate report...
}
}
// Usage
container.call(
generator,
'generateReport',
{'report': report, 'date': DateTime.now()}
);
```
## Migration Strategies
### 1. Gradual Migration
```dart
// Step 1: Update bindings
class ServiceRegistry {
void register(Container container) {
// Old way (still works)
container.bind<OldService>(OldService());
// New way
container.bind<NewService>((c) => NewService());
// Add contextual bindings
container.when(NewController)
.needs<Service>()
.give((c) => NewService());
}
}
// Step 2: Update resolution
class ServiceConsumer {
void process() {
// Old way (still works)
var oldService = container.make<OldService>();
// New way
var newService = container.make<NewService>();
var contextual = container.make<Service>(
context: NewController
);
}
}
// Step 3: Add method injection
class ServiceProcessor {
// Old way
void processOld(Data data) {
var service = container.make<Service>();
service.process(data);
}
// New way
void processNew(
Data data,
Service service // Injected automatically
) {
service.process(data);
}
}
```
### 2. Feature-by-Feature Migration
1. **Update Bindings First**
```dart
// Update all bindings to new style
void registerBindings(Container container) {
// Update simple bindings
container.bind<Service>((c) => ServiceImpl());
// Add contextual bindings
container.when(Controller)
.needs<Service>()
.give((c) => SpecialService());
}
```
2. **Add Tagged Services**
```dart
// Group related services
void registerServices(Container container) {
// Register services
container.bind<ServiceA>((c) => ServiceA());
container.bind<ServiceB>((c) => ServiceB());
// Add tags
container.tag([ServiceA, ServiceB], 'services');
}
```
3. **Implement Method Injection**
```dart
// Convert to method injection
class UserController {
// Before
void oldProcess(User user) {
var validator = container.make<Validator>();
var logger = container.make<Logger>();
// Process...
}
// After
void newProcess(
User user,
Validator validator,
Logger logger
) {
// Process...
}
}
```
## Testing During Migration
### 1. Verify Bindings
```dart
void main() {
group('Container Migration Tests', () {
late Container container;
setUp(() {
container = Container();
registerBindings(container);
});
test('should support old-style bindings', () {
var oldService = container.make<OldService>();
expect(oldService, isNotNull);
});
test('should support new-style bindings', () {
var newService = container.make<NewService>();
expect(newService, isNotNull);
});
test('should resolve contextual bindings', () {
var service = container.make<Service>(
context: Controller
);
expect(service, isA<SpecialService>());
});
});
}
```
### 2. Verify Tagged Services
```dart
void main() {
group('Tagged Services Tests', () {
late Container container;
setUp(() {
container = Container();
registerServices(container);
});
test('should resolve tagged services', () {
var services = container.taggedAs<Service>('services');
expect(services, hasLength(2));
expect(services, contains(isA<ServiceA>()));
expect(services, contains(isA<ServiceB>()));
});
});
}
```
### 3. Verify Method Injection
```dart
void main() {
group('Method Injection Tests', () {
late Container container;
setUp(() {
container = Container();
registerServices(container);
});
test('should inject method dependencies', () {
var controller = UserController();
// Call with only required parameters
container.call(
controller,
'newProcess',
{'user': testUser}
);
// Verify injection worked
verify(() => mockValidator.validate(any)).called(1);
verify(() => mockLogger.log(any)).called(1);
});
});
}
```
## Best Practices
1. **Update Bindings Consistently**
```dart
// Good: Consistent new style
container.bind<Service>((c) => ServiceImpl());
container.singleton<Logger>((c) => FileLogger());
// Bad: Mixed styles
container.bind<Service>(ServiceImpl()); // Old style
container.singleton<Logger>((c) => FileLogger()); // New style
```
2. **Use Contextual Bindings Appropriately**
```dart
// Good: Clear context and purpose
container.when(UserController)
.needs<Storage>()
.give((c) => UserStorage());
// Bad: Unclear or overly broad context
container.when(Object)
.needs<Storage>()
.give((c) => GenericStorage());
```
3. **Organize Tagged Services**
```dart
// Good: Logical grouping
container.tag([
PerformanceReport,
SystemReport
], 'system-reports');
container.tag([
UserReport,
ActivityReport
], 'user-reports');
// Bad: Mixed concerns
container.tag([
PerformanceReport,
UserReport,
Logger,
Storage
], 'services');
```
## Common Issues and Solutions
1. **Binding Resolution Errors**
```dart
// Problem: Missing binding
var service = container.make<Service>(); // Throws error
// Solution: Register binding first
container.bind<Service>((c) => ServiceImpl());
var service = container.make<Service>(); // Works
```
2. **Contextual Binding Conflicts**
```dart
// Problem: Multiple contexts
container.when(Controller)
.needs<Service>()
.give((c) => ServiceA());
container.when(Controller) // Conflict!
.needs<Service>()
.give((c) => ServiceB());
// Solution: Use specific contexts
container.when(UserController)
.needs<Service>()
.give((c) => ServiceA());
container.when(AdminController)
.needs<Service>()
.give((c) => ServiceB());
```
3. **Method Injection Failures**
```dart
// Problem: Unresolvable parameter
void process(
CustomType param // Not registered with container
) { }
// Solution: Register or provide parameter
container.bind<CustomType>((c) => CustomType());
// or
container.call(instance, 'process', {
'param': customInstance
});
```
## Next Steps
1. Audit existing container usage
2. Plan migration phases
3. Update bindings
4. Add new features
5. Update tests
6. Document changes
Would you like me to create detailed specifications for any of these next steps?

View file

@ -0,0 +1,77 @@
# Container Package Specification
## Overview
The Container package provides a powerful dependency injection container that matches Laravel's container functionality while leveraging Dart's type system. It supports auto-wiring, contextual binding, method injection, and tagged bindings.
> **Related Documentation**
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for implementation status
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Container Gap Analysis](container_gap_analysis.md) for missing features
> - See [Testing Guide](testing_guide.md) for testing approaches
[Previous content remains the same until Core Features section, then add:]
## Core Features
> **Implementation Note**: These features are part of our Laravel compatibility effort. See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for the full feature list and status.
[Previous features content remains the same, then add:]
## Integration Examples
> **Integration Note**: These examples demonstrate common integration patterns. See [Foundation Integration Guide](foundation_integration_guide.md) for more patterns and best practices.
[Previous examples content remains the same, then add:]
## Testing
> **Testing Note**: These examples show package-specific tests. See [Testing Guide](testing_guide.md) for comprehensive testing approaches.
[Previous testing content remains the same, then add:]
## Performance Considerations
> **Performance Note**: These optimizations align with our performance targets. See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#performance-benchmarks) for specific metrics.
[Previous performance content remains the same, then add:]
## Development Guidelines
### 1. Getting Started
Before contributing to this package:
1. Review [Getting Started Guide](getting_started.md)
2. Check [Container Gap Analysis](container_gap_analysis.md)
3. Follow [Testing Guide](testing_guide.md)
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
### 2. Integration Patterns
When integrating with other packages:
1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md)
2. Check [Package Integration Map](package_integration_map.md)
3. Review [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
### 3. Testing Requirements
All contributions must:
1. Include unit tests (see [Testing Guide](testing_guide.md#unit-tests))
2. Include integration tests (see [Testing Guide](testing_guide.md#integration-tests))
3. Meet performance targets (see [Testing Guide](testing_guide.md#performance-tests))
### 4. Documentation Requirements
All changes must:
1. Update this specification if needed
2. Update [Container Gap Analysis](container_gap_analysis.md) if needed
3. Follow documentation standards in [Getting Started Guide](getting_started.md#documentation)
## Next Steps
See [Container Gap Analysis](container_gap_analysis.md) for:
1. Missing features to implement
2. Areas needing improvement
3. Integration gaps
4. Documentation gaps
Would you like me to:
1. Add more integration examples?
2. Enhance testing documentation?
3. Add performance optimizations?

View file

@ -0,0 +1,448 @@
# Contracts Package Specification
## Overview
The Contracts package defines the core interfaces and contracts that form the foundation of the framework. These contracts ensure consistency and interoperability between components while enabling loose coupling and dependency injection.
> **Related Documentation**
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for implementation status
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Testing Guide](testing_guide.md) for testing approaches
> - See [Getting Started Guide](getting_started.md) for development setup
## Core Contracts
### 1. Container Contracts
```dart
/// Core container interface
abstract class ContainerContract {
/// Resolves a type from the container
T make<T>([dynamic context]);
/// Binds a type to the container
void bind<T>(T Function(ContainerContract) concrete);
/// Binds a singleton to the container
void singleton<T>(T Function(ContainerContract) concrete);
/// Checks if a type is bound
bool has<T>();
/// Tags implementations for grouped resolution
void tag(List<Type> implementations, String tag);
/// Gets all implementations with a tag
List<T> tagged<T>(String tag);
/// Adds a contextual binding
void addContextualBinding(Type concrete, Type abstract, dynamic implementation);
/// Creates a new child container
ContainerContract createChild();
}
/// Interface for contextual binding
abstract class ContextualBindingBuilder {
/// Specifies the type needed in this context
ContextualNeedsBuilder needs<T>();
}
/// Interface for contextual needs
abstract class ContextualNeedsBuilder {
/// Specifies what to give for this need
void give(dynamic implementation);
}
/// Interface for service providers
abstract class ServiceProviderContract {
/// Registers services with the container
void register();
/// Bootstraps any services
Future<void> boot();
/// Gets provided services
List<Type> provides();
/// Whether provider is deferred
bool get isDeferred => false;
}
```
### 2. Event Contracts
```dart
/// Core event dispatcher interface
abstract class EventDispatcherContract {
/// Registers an event listener
void listen<T>(void Function(T event) listener);
/// Dispatches an event
Future<void> dispatch<T>(T event);
/// Registers an event subscriber
void subscribe(EventSubscriber subscriber);
/// Dispatches an event after database commit
Future<void> dispatchAfterCommit<T>(T event);
/// Gets registered listeners
List<Function> getListeners(Type event);
}
/// Interface for event subscribers
abstract class EventSubscriber {
/// Gets events to subscribe to
Map<Type, Function> subscribe();
}
/// Interface for queueable events
abstract class ShouldQueue {
/// Gets the queue to use
String get queue => 'default';
/// Gets the delay before processing
Duration? get delay => null;
/// Gets the number of tries
int get tries => 1;
}
/// Interface for broadcastable events
abstract class ShouldBroadcast {
/// Gets channels to broadcast on
List<String> broadcastOn();
/// Gets event name for broadcasting
String broadcastAs() => runtimeType.toString();
/// Gets broadcast data
Map<String, dynamic> get broadcastWith => {};
}
```
### 3. Queue Contracts
```dart
/// Core queue interface
abstract class QueueContract {
/// Pushes a job onto the queue
Future<String> push(dynamic job, [String? queue]);
/// Pushes a job with delay
Future<String> later(Duration delay, dynamic job, [String? queue]);
/// Gets next job from queue
Future<Job?> pop([String? queue]);
/// Creates a job batch
Batch batch(List<Job> jobs);
/// Gets a queue connection
QueueConnection connection([String? name]);
}
/// Interface for queue jobs
abstract class Job {
/// Unique job ID
String get id;
/// Job payload
Map<String, dynamic> get payload;
/// Number of attempts
int get attempts;
/// Maximum tries
int get tries => 1;
/// Timeout in seconds
int get timeout => 60;
/// Executes the job
Future<void> handle();
/// Handles job failure
Future<void> failed([Exception? exception]);
}
/// Interface for job batches
abstract class Batch {
/// Batch ID
String get id;
/// Jobs in batch
List<Job> get jobs;
/// Adds jobs to batch
void add(List<Job> jobs);
/// Dispatches the batch
Future<void> dispatch();
}
```
### 4. Bus Contracts
```dart
/// Core command bus interface
abstract class CommandBusContract {
/// Dispatches a command
Future<dynamic> dispatch(Command command);
/// Dispatches a command now
Future<dynamic> dispatchNow(Command command);
/// Dispatches a command to queue
Future<dynamic> dispatchToQueue(Command command);
/// Creates a command batch
PendingBatch batch(List<Command> commands);
/// Creates a command chain
PendingChain chain(List<Command> commands);
}
/// Interface for commands
abstract class Command {
/// Gets command handler
Type get handler;
}
/// Interface for command handlers
abstract class Handler<T extends Command> {
/// Handles the command
Future<dynamic> handle(T command);
}
/// Interface for command batches
abstract class PendingBatch {
/// Dispatches the batch
Future<void> dispatch();
/// Allows failures
PendingBatch allowFailures();
}
```
### 5. Pipeline Contracts
```dart
/// Core pipeline interface
abstract class PipelineContract<T> {
/// Sends value through pipeline
PipelineContract<T> send(T passable);
/// Sets the pipes
PipelineContract<T> through(List<PipeContract<T>> pipes);
/// Processes the pipeline
Future<R> then<R>(Future<R> Function(T) destination);
}
/// Interface for pipes
abstract class PipeContract<T> {
/// Handles the passable
Future<dynamic> handle(T passable, Function next);
}
/// Interface for pipeline hub
abstract class PipelineHubContract {
/// Gets a pipeline
PipelineContract<T> pipeline<T>(String name);
/// Sets default pipes
void defaults(List<PipeContract> pipes);
}
```
## Usage Examples
### Container Usage
```dart
// Register service provider
class AppServiceProvider implements ServiceProviderContract {
@override
void register() {
container.bind<UserService>((c) => UserService(
c.make<DatabaseConnection>(),
c.make<CacheContract>()
));
}
@override
Future<void> boot() async {
// Bootstrap services
}
}
```
### Event Handling
```dart
// Define event
class OrderShipped implements ShouldQueue, ShouldBroadcast {
final Order order;
@override
List<String> broadcastOn() => ['orders.${order.id}'];
@override
String get queue => 'notifications';
}
// Handle event
dispatcher.listen<OrderShipped>((event) async {
await notifyCustomer(event.order);
});
```
### Command Bus Usage
```dart
// Define command
class CreateOrder implements Command {
final String customerId;
final List<String> products;
@override
Type get handler => CreateOrderHandler;
}
// Handle command
class CreateOrderHandler implements Handler<CreateOrder> {
@override
Future<Order> handle(CreateOrder command) async {
// Create order
}
}
// Dispatch command
var order = await bus.dispatch(CreateOrder(
customerId: '123',
products: ['abc', 'xyz']
));
```
## Testing
```dart
void main() {
group('Event Dispatcher', () {
test('dispatches after commit', () async {
var dispatcher = MockEventDispatcher();
var db = MockDatabase();
await db.transaction((tx) async {
await dispatcher.dispatchAfterCommit(OrderShipped(order));
});
verify(() => dispatcher.dispatch(any())).called(1);
});
});
group('Command Bus', () {
test('handles command batch', () async {
var bus = MockCommandBus();
await bus.batch([
CreateOrder(...),
UpdateInventory(...)
]).dispatch();
verify(() => bus.dispatchNow(any())).called(2);
});
});
}
```
## Contract Guidelines
1. **Keep Contracts Minimal**
```dart
// Good: Focused contract
abstract class Cache {
Future<T?> get<T>(String key);
Future<void> put<T>(String key, T value);
}
// Bad: Too many responsibilities
abstract class Cache {
Future<T?> get<T>(String key);
Future<void> put<T>(String key, T value);
void clearMemory();
void optimizeStorage();
void defragment();
}
```
2. **Use Type Parameters**
```dart
// Good: Type safe
abstract class Repository<T> {
Future<T?> find(String id);
Future<void> save(T entity);
}
// Bad: Dynamic typing
abstract class Repository {
Future<dynamic> find(String id);
Future<void> save(dynamic entity);
}
```
3. **Document Contracts**
```dart
/// Contract for caching implementations.
///
/// Implementations must:
/// - Handle serialization
/// - Be thread-safe
/// - Support TTL
abstract class Cache {
/// Gets a value from cache.
///
/// Returns null if not found.
/// Throws [CacheException] on error.
Future<T?> get<T>(String key);
}
```
## Next Steps
1. Implement core contracts
2. Add integration tests
3. Document Laravel compatibility
4. Add migration guides
5. Create examples
6. Write tests
Would you like me to enhance any other package specifications?
## Development Guidelines
### 1. Getting Started
Before implementing contracts:
1. Review [Getting Started Guide](getting_started.md)
2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Follow [Testing Guide](testing_guide.md)
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
### 2. Implementation Process
For each contract:
1. Write tests following [Testing Guide](testing_guide.md)
2. Implement following Laravel patterns
3. Document following [Getting Started Guide](getting_started.md#documentation)
4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md)
### 3. Quality Requirements
All implementations must:
1. Pass all tests (see [Testing Guide](testing_guide.md))
2. Meet Laravel compatibility requirements
3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md))
### 4. Integration Considerations
When implementing contracts:
1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md)
2. Ensure Laravel compatibility per [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Use testing approaches from [Testing Guide](testing_guide.md)
4. Follow development setup in [Getting Started Guide](getting_started.md)

309
docs/core_architecture.md Normal file
View file

@ -0,0 +1,309 @@
# Core Architecture
## Overview
This document explains the architectural decisions, patterns, and system design of our core package. It provides insights into how the framework components interact and how to extend the system.
> **Related Documentation**
> - See [Core Package Specification](core_package_specification.md) for implementation details
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Container Package Specification](container_package_specification.md) for dependency injection
> - See [Events Package Specification](events_package_specification.md) for event system
## Architectural Patterns
### 1. Service Container Architecture
The framework is built around a central service container that manages dependencies and provides inversion of control:
```
┌─────────────────────────────────────────┐
│ Application │
│ │
│ ┌─────────────────┐ ┌──────────────┐ │
│ │Service Container│ │Event Dispatch│ │
│ └─────────────────┘ └──────────────┘ │
│ │
│ ┌─────────────────┐ ┌──────────────┐ │
│ │Service Providers│ │ Pipeline │ │
│ └─────────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────┘
```
Key aspects:
- Central service container manages all dependencies
- Service providers bootstrap framework services
- Event system enables loose coupling
- Pipeline pattern for request/response handling
### 2. Request Lifecycle
The request flows through several layers:
```
┌──────────┐ ┌────────────┐ ┌─────────────┐
│ Server │ -> │HTTP Kernel │ -> │ Pipeline │
└──────────┘ └────────────┘ └─────────────┘
|
┌──────────┐ ┌────────────┐ ┌─────▼─────┐
│ Response │ <- Controller <- Router
└──────────┘ └────────────┘ └───────────┘
```
Stages:
1. Server receives HTTP request
2. HTTP Kernel applies global middleware
3. Pipeline processes middleware stack
4. Router matches route
5. Controller handles request
6. Response flows back through layers
### 3. Service Provider Pattern
Service providers bootstrap framework components:
```
┌─────────────────┐
│ Application │
└───────┬─────────┘
|
┌───────▼─────────┐
│Register Providers│
└───────┬─────────┘
|
┌───────▼─────────┐
│ Boot Providers │
└───────┬─────────┘
|
┌───────▼─────────┐
│ Ready to Handle │
└─────────────────┘
```
Process:
1. Register core providers
2. Register package providers
3. Register application providers
4. Boot all providers
5. Application ready
### 4. Event-Driven Architecture
Events enable loose coupling between components:
```
┌────────────┐ ┌─────────────┐ ┌──────────┐
│ Dispatcher │ -> │ Events │ -> │Listeners │
└────────────┘ └─────────────┘ └──────────┘
|
┌────────────┐ ┌─────────────┐ ┌──────────┐
│ Queued │ <- Handler <- Process
└────────────┘ └─────────────┘ └──────────┘
```
Features:
- Event dispatching
- Synchronous/async listeners
- Event queueing
- Event subscribers
- Event broadcasting
## Extension Points
### 1. Service Providers
Create custom service providers to:
- Register services
- Bootstrap components
- Configure framework
- Add middleware
- Register routes
```dart
class CustomServiceProvider extends ServiceProvider {
@override
void register() {
// Register services
}
@override
void boot() {
// Bootstrap components
}
}
```
### 2. Middleware
Add middleware to:
- Process requests
- Modify responses
- Handle authentication
- Rate limiting
- Custom processing
```dart
class CustomMiddleware implements Middleware {
Future<Response> handle(Request request, Next next) async {
// Process request
var response = await next(request);
// Modify response
return response;
}
}
```
### 3. Event Listeners
Create event listeners to:
- React to system events
- Handle async tasks
- Integrate external systems
- Add logging/monitoring
- Custom processing
```dart
class CustomListener {
void handle(CustomEvent event) {
// Handle event
}
}
```
### 4. Console Commands
Add console commands to:
- Run maintenance tasks
- Process queues
- Generate files
- Custom CLI tools
- System management
```dart
class CustomCommand extends Command {
String get name => 'custom:command';
Future<void> handle() async {
// Command logic
}
}
```
## Package Integration
### 1. Core Package Dependencies
```
┌─────────────┐
│ Core │
└─────┬───────┘
|
┌─────▼───────┐ ┌────────────┐
│ Container │ --> │ Events │
└─────────────┘ └────────────┘
|
┌─────▼───────┐ ┌────────────┐
│ Pipeline │ --> │ Route │
└─────────────┘ └────────────┘
```
### 2. Optional Package Integration
```
┌─────────────┐
│ Core │
└─────┬───────┘
|
┌─────▼───────┐ ┌────────────┐
│ Queue │ --> │ Bus │
└─────────────┘ └────────────┘
|
┌─────▼───────┐ ┌────────────┐
│ Cache │ --> │ Mail │
└─────────────┘ └────────────┘
```
## Performance Considerations
### 1. Service Container
- Optimize bindings
- Use singletons where appropriate
- Lazy load services
- Cache resolved instances
### 2. Request Handling
- Efficient middleware pipeline
- Route caching
- Response caching
- Resource pooling
### 3. Event System
- Async event processing
- Event batching
- Queue throttling
- Listener optimization
### 4. Memory Management
- Clean up resources
- Limit instance caching
- Monitor memory usage
- Handle memory pressure
## Security Considerations
### 1. Request Validation
- Input sanitization
- CSRF protection
- XSS prevention
- SQL injection prevention
### 2. Authentication
- Secure session handling
- Token management
- Password hashing
- Rate limiting
### 3. Authorization
- Role-based access
- Permission checking
- Policy enforcement
- Resource protection
### 4. Data Protection
- Encryption at rest
- Secure communication
- Data sanitization
- Audit logging
## Development Guidelines
### 1. Core Development
- Follow framework patterns
- Maintain backward compatibility
- Document changes
- Write tests
- Consider performance
### 2. Package Development
- Use service providers
- Integrate with events
- Follow naming conventions
- Add package tests
- Document features
### 3. Application Development
- Use dependency injection
- Handle events properly
- Follow middleware patterns
- Write clean code
- Test thoroughly
## Next Steps
1. Review architecture with team
2. Document design decisions
3. Create development guides
4. Set up monitoring
5. Plan optimizations
6. Schedule security review

View file

@ -0,0 +1,556 @@
# Core Package Specification
## Overview
The Core package provides the foundation and entry point for our framework. It manages the application lifecycle, bootstraps services, handles HTTP requests, and coordinates all other framework packages.
> **Related Documentation**
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for implementation status
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Testing Guide](testing_guide.md) for testing approaches
> - See [Getting Started Guide](getting_started.md) for development setup
> - See [Container Package Specification](container_package_specification.md) for dependency injection
> - See [Events Package Specification](events_package_specification.md) for application events
## Core Features
### 1. Application
```dart
/// Core application class
class Application {
/// Container instance
final Container _container;
/// Service providers
final List<ServiceProvider> _providers = [];
/// Booted flag
bool _booted = false;
/// Environment
late final String environment;
/// Base path
late final String basePath;
Application(this._container) {
_container.instance<Application>(this);
_registerBaseBindings();
_registerCoreProviders();
}
/// Registers base bindings
void _registerBaseBindings() {
_container.instance<Container>(_container);
_container.instance<String>('base_path', basePath);
_container.instance<String>('env', environment);
}
/// Registers core providers
void _registerCoreProviders() {
register(EventServiceProvider());
register(LogServiceProvider());
register(RoutingServiceProvider());
register(ConfigServiceProvider());
}
/// Registers a service provider
void register(ServiceProvider provider) {
provider.app = this;
provider.register();
_providers.add(provider);
if (_booted) {
_bootProvider(provider);
}
}
/// Boots the application
Future<void> boot() async {
if (_booted) return;
for (var provider in _providers) {
await _bootProvider(provider);
}
_booted = true;
}
/// Boots a provider
Future<void> _bootProvider(ServiceProvider provider) async {
await provider.callBootingCallbacks();
await provider.boot();
await provider.callBootedCallbacks();
}
/// Handles HTTP request
Future<Response> handle(Request request) async {
try {
return await _pipeline.handle(request);
} catch (e) {
return _handleError(e, request);
}
}
/// Gets container instance
Container get container => _container;
/// Makes instance from container
T make<T>([dynamic parameters]) {
return _container.make<T>(parameters);
}
/// Gets environment
bool environment(String env) {
return this.environment == env;
}
/// Determines if application is in production
bool get isProduction => environment == 'production';
/// Determines if application is in development
bool get isDevelopment => environment == 'development';
/// Determines if application is in testing
bool get isTesting => environment == 'testing';
/// Gets base path
String path([String? path]) {
return [basePath, path].where((p) => p != null).join('/');
}
}
```
### 2. Service Providers
```dart
/// Base service provider
abstract class ServiceProvider {
/// Application instance
late Application app;
/// Container instance
Container get container => app.container;
/// Booting callbacks
final List<Function> _bootingCallbacks = [];
/// Booted callbacks
final List<Function> _bootedCallbacks = [];
/// Registers services
void register();
/// Boots services
Future<void> boot() async {}
/// Registers booting callback
void booting(Function callback) {
_bootingCallbacks.add(callback);
}
/// Registers booted callback
void booted(Function callback) {
_bootedCallbacks.add(callback);
}
/// Calls booting callbacks
Future<void> callBootingCallbacks() async {
for (var callback in _bootingCallbacks) {
await callback(app);
}
}
/// Calls booted callbacks
Future<void> callBootedCallbacks() async {
for (var callback in _bootedCallbacks) {
await callback(app);
}
}
}
/// Event service provider
class EventServiceProvider extends ServiceProvider {
@override
void register() {
container.singleton<EventDispatcherContract>((c) =>
EventDispatcher(c)
);
}
}
/// Routing service provider
class RoutingServiceProvider extends ServiceProvider {
@override
void register() {
container.singleton<RouterContract>((c) =>
Router(c)
);
}
@override
Future<void> boot() async {
var router = container.make<RouterContract>();
await loadRoutes(router);
}
}
```
### 3. HTTP Kernel
```dart
/// HTTP kernel
class HttpKernel {
/// Application instance
final Application _app;
/// Global middleware
final List<dynamic> middleware = [
CheckForMaintenanceMode::class,
ValidatePostSize::class,
TrimStrings::class,
ConvertEmptyStringsToNull::class
];
/// Route middleware groups
final Map<String, List<dynamic>> middlewareGroups = {
'web': [
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartSession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
SubstituteBindings::class
],
'api': [
'throttle:60,1',
SubstituteBindings::class
]
};
/// Route middleware aliases
final Map<String, dynamic> routeMiddleware = {
'auth': Authenticate::class,
'auth.basic': AuthenticateWithBasicAuth::class,
'bindings': SubstituteBindings::class,
'cache.headers': SetCacheHeaders::class,
'can': Authorize::class,
'guest': RedirectIfAuthenticated::class,
'signed': ValidateSignature::class,
'throttle': ThrottleRequests::class,
'verified': EnsureEmailIsVerified::class,
};
HttpKernel(this._app);
/// Handles HTTP request
Future<Response> handle(Request request) async {
try {
request = await _handleGlobalMiddleware(request);
return await _app.handle(request);
} catch (e) {
return _handleError(e, request);
}
}
/// Handles global middleware
Future<Request> _handleGlobalMiddleware(Request request) async {
var pipeline = _app.make<Pipeline>();
return await pipeline
.send(request)
.through(middleware)
.then((request) => request);
}
/// Handles error
Response _handleError(Object error, Request request) {
var handler = _app.make<ExceptionHandler>();
return handler.render(error, request);
}
}
```
### 4. Console Kernel
```dart
/// Console kernel
class ConsoleKernel {
/// Application instance
final Application _app;
/// Console commands
final List<dynamic> commands = [
// Framework Commands
KeyGenerateCommand::class,
ConfigCacheCommand::class,
ConfigClearCommand::class,
RouteListCommand::class,
RouteCacheCommand::class,
RouteClearCommand::class,
// App Commands
SendEmailsCommand::class,
PruneOldRecordsCommand::class
];
/// Command schedules
final Map<String, String> schedules = {
'emails:send': '0 * * * *',
'records:prune': '0 0 * * *'
};
ConsoleKernel(this._app);
/// Handles console command
Future<int> handle(List<String> args) async {
try {
var status = await _runCommand(args);
return status ?? 0;
} catch (e) {
_handleError(e);
return 1;
}
}
/// Runs console command
Future<int?> _runCommand(List<String> args) async {
var command = _resolveCommand(args);
if (command == null) return null;
return await command.run(args);
}
/// Resolves command from arguments
Command? _resolveCommand(List<String> args) {
if (args.isEmpty) return null;
var name = args.first;
var command = commands.firstWhere(
(c) => c.name == name,
orElse: () => null
);
if (command == null) return null;
return _app.make<Command>(command);
}
/// Handles error
void _handleError(Object error) {
stderr.writeln(error);
}
}
```
### 5. Exception Handler
```dart
/// Exception handler
class ExceptionHandler {
/// Application instance
final Application _app;
/// Exception renderers
final Map<Type, Function> _renderers = {
ValidationException: _renderValidationException,
AuthenticationException: _renderAuthenticationException,
AuthorizationException: _renderAuthorizationException,
NotFoundException: _renderNotFoundException,
HttpException: _renderHttpException
};
ExceptionHandler(this._app);
/// Renders exception to response
Response render(Object error, Request request) {
var renderer = _renderers[error.runtimeType];
if (renderer != null) {
return renderer(error, request);
}
return _renderGenericException(error, request);
}
/// Renders validation exception
Response _renderValidationException(
ValidationException e,
Request request
) {
if (request.wantsJson) {
return Response.json({
'message': 'The given data was invalid.',
'errors': e.errors
}, 422);
}
return Response.redirect()
.back()
.withErrors(e.errors)
.withInput(request.all());
}
/// Renders generic exception
Response _renderGenericException(Object e, Request request) {
if (_app.isProduction) {
return Response('Server Error', 500);
}
return Response(e.toString(), 500);
}
}
```
## Integration Examples
### 1. Application Bootstrap
```dart
void main() async {
var container = Container();
var app = Application(container)
..environment = 'production'
..basePath = Directory.current.path;
await app.boot();
var server = HttpServer(app);
await server.start();
}
```
### 2. Service Provider
```dart
class AppServiceProvider extends ServiceProvider {
@override
void register() {
container.singleton<UserRepository>((c) =>
DatabaseUserRepository(c.make<Database>())
);
}
@override
Future<void> boot() async {
var config = container.make<ConfigContract>();
TimeZone.setDefault(config.get('app.timezone'));
}
}
```
### 3. HTTP Request Handling
```dart
class Server {
final HttpKernel kernel;
Future<void> handle(HttpRequest request) async {
var protevusRequest = await Request.fromHttpRequest(request);
var response = await kernel.handle(protevusRequest);
await response.send(request.response);
}
}
```
## Testing
```dart
void main() {
group('Application', () {
test('boots providers', () async {
var app = Application(Container());
var provider = TestProvider();
app.register(provider);
await app.boot();
expect(provider.booted, isTrue);
});
test('handles requests', () async {
var app = Application(Container());
await app.boot();
var request = Request('GET', '/');
var response = await app.handle(request);
expect(response.statusCode, equals(200));
});
});
group('Service Provider', () {
test('registers services', () async {
var app = Application(Container());
var provider = TestProvider();
app.register(provider);
expect(app.make<TestService>(), isNotNull);
});
});
}
```
## Next Steps
1. Implement core application
2. Add service providers
3. Add HTTP kernel
4. Add console kernel
5. Write tests
6. Add benchmarks
## Development Guidelines
### 1. Getting Started
Before implementing core features:
1. Review [Getting Started Guide](getting_started.md)
2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Follow [Testing Guide](testing_guide.md)
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
5. Review [Container Package Specification](container_package_specification.md)
6. Review [Events Package Specification](events_package_specification.md)
### 2. Implementation Process
For each core feature:
1. Write tests following [Testing Guide](testing_guide.md)
2. Implement following framework patterns
3. Document following [Getting Started Guide](getting_started.md#documentation)
4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md)
### 3. Quality Requirements
All implementations must:
1. Pass all tests (see [Testing Guide](testing_guide.md))
2. Follow framework patterns
3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md))
4. Support dependency injection (see [Container Package Specification](container_package_specification.md))
5. Support event system (see [Events Package Specification](events_package_specification.md))
### 4. Integration Considerations
When implementing core features:
1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md)
2. Use framework patterns consistently
3. Use testing approaches from [Testing Guide](testing_guide.md)
4. Follow development setup in [Getting Started Guide](getting_started.md)
### 5. Performance Guidelines
Core system must:
1. Boot efficiently
2. Handle requests quickly
3. Manage memory usage
4. Scale horizontally
5. Meet performance targets in [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#performance-benchmarks)
### 6. Testing Requirements
Core tests must:
1. Cover all core features
2. Test application lifecycle
3. Verify service providers
4. Check error handling
5. Follow patterns in [Testing Guide](testing_guide.md)
### 7. Documentation Requirements
Core documentation must:
1. Explain framework patterns
2. Show lifecycle examples
3. Cover error handling
4. Include performance tips
5. Follow standards in [Getting Started Guide](getting_started.md#documentation)

295
docs/events_gap_analysis.md Normal file
View file

@ -0,0 +1,295 @@
# Events Package Gap Analysis
## Overview
This document analyzes the gaps between our Events package's actual implementation and our documentation, identifying areas that need implementation or documentation updates.
> **Related Documentation**
> - See [Events Package Specification](events_package_specification.md) for current implementation
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for overall status
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Testing Guide](testing_guide.md) for testing approaches
> - See [Getting Started Guide](getting_started.md) for development setup
## Implementation Gaps
### 1. Missing Laravel Features
```dart
// Documented but not implemented:
// 1. Event Discovery
class EventDiscovery {
// Need to implement:
Map<Type, Function> discoverHandlers(Type type);
void discoverEvents(String path);
}
// 2. After Commit Handling
class DatabaseEventDispatcher {
// Need to implement:
Future<void> dispatchAfterCommit<T>(T event);
void afterCommit(Function callback);
}
// 3. Better Broadcasting
class BroadcastManager {
// Need to implement:
Channel privateChannel(String name);
PresenceChannel presenceChannel(String name);
Future<void> broadcast(List<String> channels, String event, dynamic data);
}
```
### 2. Existing Features Not Documented
```dart
// Implemented but not documented:
// 1. Wildcard Event Listeners
class Dispatcher {
/// Adds wildcard event listener
void _setupWildcardListen(String event, Function listener) {
_wildcards.putIfAbsent(event, () => []).add(listener);
_wildcardsCache.clear();
}
/// Gets wildcard listeners
List<Function> _getWildcardListeners(String eventName);
}
// 2. Event Bus Integration
class Dispatcher {
/// EventBus integration
final EventBus _eventBus;
final Map<String, StreamSubscription> _eventBusSubscriptions = {};
/// Subscribes to EventBus
void subscribe(EventBusSubscriber subscriber);
}
// 3. Message Queue Integration
class Dispatcher {
/// MQ integration
late final MQClient? _mqClient;
/// Queue setup
void _setupQueuesAndExchanges();
void _startProcessingQueuedEvents();
}
```
### 3. Integration Points Not Documented
```dart
// 1. Container Integration
class Dispatcher {
/// Container reference
final Container container;
/// Queue resolver
late final Function _queueResolver;
/// Transaction manager resolver
late final Function _transactionManagerResolver;
}
// 2. ReactiveX Integration
class Dispatcher {
/// Subject management
final Map<String, BehaviorSubject<dynamic>> _subjects = {};
/// Stream access
Stream<T> on<T>(String event);
}
// 3. Resource Management
class Dispatcher {
/// Cleanup
Future<void> close();
void dispose();
}
```
## Documentation Gaps
### 1. Missing API Documentation
```dart
// Need to document:
/// Listens for events using wildcard patterns.
///
/// Example:
/// ```dart
/// dispatcher.listen('user.*', (event, data) {
/// // Handles all user events
/// });
/// ```
void listen(String pattern, Function listener);
/// Subscribes to event streams using ReactiveX.
///
/// Example:
/// ```dart
/// dispatcher.on<UserCreated>('user.created')
/// .listen((event) {
/// // Handle user created event
/// });
/// ```
Stream<T> on<T>(String event);
```
### 2. Missing Integration Examples
```dart
// Need examples for:
// 1. EventBus Integration
var subscriber = MyEventSubscriber();
dispatcher.subscribe(subscriber);
// 2. Message Queue Integration
dispatcher.setMQClient(mqClient);
await dispatcher.push('user.created', userData);
// 3. ReactiveX Integration
dispatcher.on<UserEvent>('user.*')
.where((e) => e.type == 'premium')
.listen((e) => handlePremiumUser(e));
```
### 3. Missing Test Coverage
```dart
// Need tests for:
void main() {
group('Wildcard Events', () {
test('matches wildcard patterns', () {
var dispatcher = Dispatcher(container);
var received = <String>[];
dispatcher.listen('user.*', (event, _) {
received.add(event);
});
await dispatcher.dispatch('user.created');
await dispatcher.dispatch('user.updated');
expect(received, ['user.created', 'user.updated']);
});
});
group('Queue Integration', () {
test('queues events properly', () async {
var dispatcher = Dispatcher(container);
dispatcher.setMQClient(mockClient);
await dispatcher.push('delayed.event', data);
verify(() => mockClient.sendMessage(
exchangeName: any,
routingKey: any,
message: any
)).called(1);
});
});
}
```
## Implementation Priority
1. **High Priority**
- Event discovery (Laravel compatibility)
- After commit handling (Laravel compatibility)
- Better broadcasting support
2. **Medium Priority**
- Better queue integration
- Enhanced wildcard support
- Performance optimizations
3. **Low Priority**
- Additional helper methods
- Extended testing utilities
- Debug/profiling tools
## Next Steps
1. **Implementation Tasks**
- Add event discovery
- Add after commit handling
- Enhance broadcasting
- Improve queue integration
2. **Documentation Tasks**
- Document wildcard events
- Document EventBus integration
- Document MQ integration
- Add integration examples
3. **Testing Tasks**
- Add wildcard event tests
- Add queue integration tests
- Add ReactiveX integration tests
- Add resource cleanup tests
Would you like me to:
1. Start implementing missing features?
2. Update documentation for existing features?
3. Create test cases for missing coverage?
## Development Guidelines
### 1. Getting Started
Before implementing event features:
1. Review [Getting Started Guide](getting_started.md)
2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Follow [Testing Guide](testing_guide.md)
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
5. Review [Events Package Specification](events_package_specification.md)
### 2. Implementation Process
For each event feature:
1. Write tests following [Testing Guide](testing_guide.md)
2. Implement following Laravel patterns
3. Document following [Getting Started Guide](getting_started.md#documentation)
4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md)
### 3. Quality Requirements
All implementations must:
1. Pass all tests (see [Testing Guide](testing_guide.md))
2. Meet Laravel compatibility requirements
3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md))
4. Match specifications in [Events Package Specification](events_package_specification.md)
### 4. Integration Considerations
When implementing event features:
1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md)
2. Ensure Laravel compatibility per [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Use testing approaches from [Testing Guide](testing_guide.md)
4. Follow development setup in [Getting Started Guide](getting_started.md)
### 5. Performance Guidelines
Events system must:
1. Handle high event throughput
2. Minimize memory usage
3. Support async operations
4. Scale horizontally
5. Meet performance targets in [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#performance-benchmarks)
### 6. Testing Requirements
Event tests must:
1. Cover all event scenarios
2. Test async behavior
3. Verify queue integration
4. Check broadcasting
5. Follow patterns in [Testing Guide](testing_guide.md)
### 7. Documentation Requirements
Event documentation must:
1. Explain event patterns
2. Show integration examples
3. Cover error handling
4. Include performance tips
5. Follow standards in [Getting Started Guide](getting_started.md#documentation)

View file

@ -0,0 +1,453 @@
# Events Package Specification
## Overview
The Events package provides a robust event system that matches Laravel's event functionality while leveraging Dart's async capabilities. It integrates with our Queue, Bus, and Database packages to provide a complete event handling solution.
> **Related Documentation**
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for implementation status
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Testing Guide](testing_guide.md) for testing approaches
> - See [Getting Started Guide](getting_started.md) for development setup
> - See [Contracts Package Specification](contracts_package_specification.md) for event contracts
## Core Features
### 1. Event Dispatcher
```dart
/// Core event dispatcher implementation
class EventDispatcher implements EventDispatcherContract {
final Container _container;
final Map<Type, List<EventListener>> _listeners = {};
final List<EventSubscriber> _subscribers = {};
final QueueContract? _queue;
final BroadcasterContract? _broadcaster;
final List<dynamic> _afterCommitEvents = [];
EventDispatcher(
this._container, {
QueueContract? queue,
BroadcasterContract? broadcaster
}) : _queue = queue,
_broadcaster = broadcaster;
@override
void listen<T>(void Function(T event) listener) {
_listeners.putIfAbsent(T, () => []).add(
EventListener<T>(listener)
);
}
@override
Future<void> dispatch<T>(T event) async {
var listeners = _listeners[T] ?? [];
// Handle after commit events
if (event is ShouldDispatchAfterCommit && _isWithinTransaction()) {
_afterCommitEvents.add(event);
return;
}
// Handle queued events
if (event is ShouldQueue && _queue != null) {
await _queueEvent(event, listeners);
return;
}
// Handle broadcasting
if (event is ShouldBroadcast && _broadcaster != null) {
await _broadcastEvent(event);
}
// Notify listeners
await _notifyListeners(event, listeners);
}
@override
Future<void> dispatchAfterCommit<T>(T event) async {
if (_isWithinTransaction()) {
_afterCommitEvents.add(event);
} else {
await dispatch(event);
}
}
bool _isWithinTransaction() {
if (_container.has<DatabaseManager>()) {
var db = _container.make<DatabaseManager>();
return db.transactionLevel > 0;
}
return false;
}
Future<void> _dispatchAfterCommitEvents() async {
var events = List.from(_afterCommitEvents);
_afterCommitEvents.clear();
for (var event in events) {
await dispatch(event);
}
}
}
```
### 2. Event Discovery
```dart
/// Discovers event handlers through reflection and attributes
class EventDiscovery {
final Container _container;
final Reflector _reflector;
EventDiscovery(this._container, this._reflector);
/// Discovers event handlers in a directory
Future<void> discoverEvents(String path) async {
var files = Directory(path).listSync(recursive: true);
for (var file in files) {
if (file.path.endsWith('.dart')) {
await _processFile(file.path);
}
}
}
Future<void> _processFile(String path) async {
var library = await _reflector.loadLibrary(path);
for (var type in library.declarations.values) {
if (type is ClassMirror) {
_processClass(type);
}
}
}
void _processClass(ClassMirror classMirror) {
// Find @Handles annotations
for (var method in classMirror.declarations.values) {
if (method is MethodMirror) {
var handles = method.metadata
.firstWhere((m) => m.type == Handles,
orElse: () => null);
if (handles != null) {
var eventType = handles.getField('event').reflectee;
_registerHandler(classMirror.reflectedType, method.simpleName, eventType);
}
}
}
}
void _registerHandler(Type classType, Symbol methodName, Type eventType) {
var instance = _container.make(classType);
var dispatcher = _container.make<EventDispatcherContract>();
dispatcher.listen(eventType, (event) {
var mirror = reflect(instance);
mirror.invoke(methodName, [event]);
});
}
}
```
### 3. Event Broadcasting
```dart
/// Contract for event broadcasters
abstract class BroadcasterContract {
/// Broadcasts an event
Future<void> broadcast(
List<String> channels,
String eventName,
dynamic data
);
/// Creates a private channel
Channel privateChannel(String name);
/// Creates a presence channel
PresenceChannel presenceChannel(String name);
}
/// Pusher event broadcaster
class PusherBroadcaster implements BroadcasterContract {
final PusherClient _client;
final AuthManager _auth;
PusherBroadcaster(this._client, this._auth);
@override
Future<void> broadcast(
List<String> channels,
String eventName,
dynamic data
) async {
for (var channel in channels) {
await _client.trigger(channel, eventName, data);
}
}
@override
Channel privateChannel(String name) {
return PrivateChannel(_client, _auth, name);
}
@override
PresenceChannel presenceChannel(String name) {
return PresenceChannel(_client, _auth, name);
}
}
```
### 4. Integration with Queue
```dart
/// Job for processing queued events
class QueuedEventJob implements Job {
final dynamic event;
final List<EventListener> listeners;
final Map<String, dynamic> data;
QueuedEventJob({
required this.event,
required this.listeners,
this.data = const {}
});
@override
Future<void> handle() async {
for (var listener in listeners) {
try {
await listener.handle(event);
} catch (e) {
await _handleFailure(e);
}
}
}
@override
Future<void> failed([Exception? e]) async {
if (event is FailedEventHandler) {
await (event as FailedEventHandler).failed(e);
}
}
@override
int get tries => event is HasTries ? (event as HasTries).tries : 1;
@override
Duration? get timeout =>
event is HasTimeout ? (event as HasTimeout).timeout : null;
}
```
### 5. Integration with Bus
```dart
/// Event command for command bus integration
class EventCommand implements Command {
final dynamic event;
final List<EventListener> listeners;
EventCommand(this.event, this.listeners);
@override
Type get handler => EventCommandHandler;
}
/// Handler for event commands
class EventCommandHandler implements Handler<EventCommand> {
final EventDispatcher _events;
EventCommandHandler(this._events);
@override
Future<void> handle(EventCommand command) async {
await _events._notifyListeners(
command.event,
command.listeners
);
}
}
```
## Usage Examples
### Basic Event Handling
```dart
// Define event
class OrderShipped {
final Order order;
OrderShipped(this.order);
}
// Create listener
@Handles(OrderShipped)
class OrderShippedListener {
void handle(OrderShipped event) {
// Handle event
}
}
// Register and dispatch
dispatcher.listen<OrderShipped>((event) {
// Handle event
});
await dispatcher.dispatch(OrderShipped(order));
```
### After Commit Events
```dart
class OrderCreated implements ShouldDispatchAfterCommit {
final Order order;
OrderCreated(this.order);
}
// In transaction
await db.transaction((tx) async {
var order = await tx.create(orderData);
await dispatcher.dispatchAfterCommit(OrderCreated(order));
});
```
### Broadcasting
```dart
class MessageSent implements ShouldBroadcast {
final Message message;
@override
List<String> broadcastOn() => [
'private-chat.${message.roomId}'
];
@override
Map<String, dynamic> get broadcastWith => {
'id': message.id,
'content': message.content,
'user': message.user.toJson()
};
}
// Create private channel
var channel = broadcaster.privateChannel('chat.123');
await channel.whisper('typing', {'user': 'john'});
```
### Queue Integration
```dart
class ProcessOrder implements ShouldQueue {
final Order order;
@override
String get queue => 'orders';
@override
int get tries => 3;
@override
Duration get timeout => Duration(minutes: 5);
}
// Dispatch queued event
await dispatcher.dispatch(ProcessOrder(order));
```
## Testing
```dart
void main() {
group('Event Dispatcher', () {
test('dispatches after commit', () async {
var dispatcher = MockEventDispatcher();
var db = MockDatabase();
await db.transaction((tx) async {
await dispatcher.dispatchAfterCommit(OrderShipped(order));
expect(dispatcher.hasAfterCommitEvents, isTrue);
});
expect(dispatcher.hasAfterCommitEvents, isFalse);
verify(() => dispatcher.dispatch(any())).called(1);
});
test('discovers event handlers', () async {
var discovery = EventDiscovery(container, reflector);
await discovery.discoverEvents('lib/events');
var dispatcher = container.make<EventDispatcherContract>();
await dispatcher.dispatch(OrderShipped(order));
verify(() => orderListener.handle(any())).called(1);
});
});
}
```
## Next Steps
1. Complete after commit handling
2. Enhance event discovery
3. Add more broadcast drivers
4. Improve queue integration
5. Add performance optimizations
6. Write more tests
Would you like me to enhance any other package specifications?
## Development Guidelines
### 1. Getting Started
Before implementing event features:
1. Review [Getting Started Guide](getting_started.md)
2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Follow [Testing Guide](testing_guide.md)
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
5. Understand [Contracts Package Specification](contracts_package_specification.md)
### 2. Implementation Process
For each event feature:
1. Write tests following [Testing Guide](testing_guide.md)
2. Implement following Laravel patterns
3. Document following [Getting Started Guide](getting_started.md#documentation)
4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md)
### 3. Quality Requirements
All implementations must:
1. Pass all tests (see [Testing Guide](testing_guide.md))
2. Meet Laravel compatibility requirements
3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md))
4. Implement required contracts (see [Contracts Package Specification](contracts_package_specification.md))
### 4. Integration Considerations
When implementing events:
1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md)
2. Ensure Laravel compatibility per [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Use testing approaches from [Testing Guide](testing_guide.md)
4. Follow development setup in [Getting Started Guide](getting_started.md)
5. Implement all contracts from [Contracts Package Specification](contracts_package_specification.md)
### 5. Performance Guidelines
Event system must:
1. Handle high throughput efficiently
2. Minimize memory usage
3. Support async operations
4. Scale horizontally
5. Meet performance targets in [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#performance-benchmarks)
### 6. Testing Requirements
Event tests must:
1. Cover all event scenarios
2. Test async behavior
3. Verify queue integration
4. Check broadcasting
5. Follow patterns in [Testing Guide](testing_guide.md)
### 7. Documentation Requirements
Event documentation must:
1. Explain event patterns
2. Show integration examples
3. Cover error handling
4. Include performance tips
5. Follow standards in [Getting Started Guide](getting_started.md#documentation)

View file

@ -0,0 +1,349 @@
# FileSystem Package Gap Analysis
## Overview
This document analyzes the gaps between our current filesystem handling (in Core package) and Laravel's FileSystem package functionality, identifying what needs to be implemented as a standalone FileSystem package.
> **Related Documentation**
> - See [FileSystem Package Specification](filesystem_package_specification.md) for current implementation
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for overall status
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Testing Guide](testing_guide.md) for testing approaches
> - See [Getting Started Guide](getting_started.md) for development setup
## Implementation Gaps
### 1. Missing Package Structure
```dart
// Need to create dedicated FileSystem package:
packages/filesystem/
├── lib/
│ ├── src/
│ │ ├── filesystem.dart
│ │ ├── filesystem_manager.dart
│ │ ├── drivers/
│ │ │ ├── local_driver.dart
│ │ │ ├── s3_driver.dart
│ │ │ └── gcs_driver.dart
│ │ └── contracts/
│ │ ├── filesystem.dart
│ │ └── driver.dart
│ └── filesystem.dart
├── test/
└── example/
```
### 2. Missing Core Features
```dart
// 1. Filesystem Manager
class FilesystemManager {
// Need to implement:
Filesystem disk([String? name]);
void extend(String driver, FilesystemDriver Function() callback);
FilesystemDriver createDriver(Map<String, dynamic> config);
}
// 2. Filesystem Implementation
class Filesystem {
// Need to implement:
Future<bool> exists(String path);
Future<String> get(String path);
Future<void> put(String path, dynamic contents, [Map<String, String>? options]);
Future<void> delete(String path);
Future<void> copy(String from, String to);
Future<void> move(String from, String to);
Future<String> url(String path);
Future<Stream<List<int>>> readStream(String path);
Future<void> writeStream(String path, Stream<List<int>> contents);
}
// 3. Driver Implementations
class LocalDriver {
// Need to implement:
Future<void> ensureDirectory(String path);
Future<void> setVisibility(String path, String visibility);
Future<Map<String, dynamic>> getMetadata(String path);
}
```
### 3. Missing Laravel Features
```dart
// 1. Cloud Storage
class S3Driver {
// Need to implement:
Future<void> upload(String path, dynamic contents, String visibility);
Future<String> temporaryUrl(String path, Duration expiration);
Future<void> setVisibility(String path, String visibility);
}
// 2. Directory Operations
class DirectoryOperations {
// Need to implement:
Future<List<String>> files(String directory);
Future<List<String>> allFiles(String directory);
Future<List<String>> directories(String directory);
Future<List<String>> allDirectories(String directory);
Future<void> makeDirectory(String path);
Future<void> deleteDirectory(String directory);
}
// 3. File Visibility
class VisibilityConverter {
// Need to implement:
String toOctal(String visibility);
String fromOctal(String permissions);
bool isPublic(String path);
bool isPrivate(String path);
}
```
## Integration Gaps
### 1. Container Integration
```dart
// Need to implement:
class FilesystemServiceProvider {
void register() {
// Register filesystem manager
container.singleton<FilesystemManager>((c) =>
FilesystemManager(
config: c.make<ConfigContract>()
)
);
// Register default filesystem
container.singleton<Filesystem>((c) =>
c.make<FilesystemManager>().disk()
);
}
}
```
### 2. Config Integration
```dart
// Need to implement:
// config/filesystems.dart
class FilesystemsConfig {
static Map<String, dynamic> get config => {
'default': 'local',
'disks': {
'local': {
'driver': 'local',
'root': 'storage/app'
},
's3': {
'driver': 's3',
'key': env('AWS_ACCESS_KEY_ID'),
'secret': env('AWS_SECRET_ACCESS_KEY'),
'region': env('AWS_DEFAULT_REGION'),
'bucket': env('AWS_BUCKET')
}
}
};
}
```
### 3. Event Integration
```dart
// Need to implement:
class FilesystemEvents {
// File events
static const String writing = 'filesystem.writing';
static const String written = 'filesystem.written';
static const String deleting = 'filesystem.deleting';
static const String deleted = 'filesystem.deleted';
// Directory events
static const String makingDirectory = 'filesystem.making_directory';
static const String madeDirectory = 'filesystem.made_directory';
static const String deletingDirectory = 'filesystem.deleting_directory';
static const String deletedDirectory = 'filesystem.deleted_directory';
}
```
## Documentation Gaps
### 1. Missing API Documentation
```dart
// Need to document:
/// Manages filesystem operations across multiple storage drivers.
///
/// Provides a unified API for working with files across different storage systems:
/// ```dart
/// // Store a file
/// await storage.put('avatars/user1.jpg', fileContents);
///
/// // Get a file
/// var contents = await storage.get('avatars/user1.jpg');
/// ```
class Filesystem {
/// Stores a file at the specified path.
///
/// Options can include:
/// - visibility: 'public' or 'private'
/// - mime: MIME type of the file
Future<void> put(String path, dynamic contents, [Map<String, String>? options]);
}
```
### 2. Missing Usage Examples
```dart
// Need examples for:
// 1. Basic File Operations
var storage = Storage.disk();
await storage.put('file.txt', 'Hello World');
var contents = await storage.get('file.txt');
await storage.delete('file.txt');
// 2. Stream Operations
var fileStream = File('large.zip').openRead();
await storage.writeStream('uploads/large.zip', fileStream);
var downloadStream = await storage.readStream('uploads/large.zip');
// 3. Cloud Storage
var s3 = Storage.disk('s3');
await s3.put(
'images/photo.jpg',
photoBytes,
{'visibility': 'public'}
);
var url = await s3.url('images/photo.jpg');
```
### 3. Missing Test Coverage
```dart
// Need tests for:
void main() {
group('Local Driver', () {
test('handles file operations', () async {
var storage = Filesystem(LocalDriver(root: 'storage'));
await storage.put('test.txt', 'contents');
expect(await storage.exists('test.txt'), isTrue);
expect(await storage.get('test.txt'), equals('contents'));
await storage.delete('test.txt');
expect(await storage.exists('test.txt'), isFalse);
});
});
group('S3 Driver', () {
test('handles cloud operations', () async {
var storage = Filesystem(S3Driver(config));
await storage.put('test.txt', 'contents', {
'visibility': 'public'
});
var url = await storage.url('test.txt');
expect(url, startsWith('https://'));
});
});
}
```
## Implementation Priority
1. **High Priority**
- Create FileSystem package structure
- Implement core filesystem
- Add local driver
- Add basic operations
2. **Medium Priority**
- Add cloud drivers
- Add streaming support
- Add directory operations
- Add container integration
3. **Low Priority**
- Add helper functions
- Add testing utilities
- Add debugging tools
## Next Steps
1. **Package Creation**
- Create package structure
- Move filesystem code from Core
- Add package dependencies
- Setup testing
2. **Core Implementation**
- Implement FilesystemManager
- Implement Filesystem
- Implement LocalDriver
- Add cloud drivers
3. **Integration Implementation**
- Add container integration
- Add config support
- Add event support
- Add service providers
Would you like me to:
1. Create the FileSystem package structure?
2. Start implementing core features?
3. Create detailed implementation plans?
## Development Guidelines
### 1. Getting Started
Before implementing filesystem features:
1. Review [Getting Started Guide](getting_started.md)
2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Follow [Testing Guide](testing_guide.md)
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
5. Review [FileSystem Package Specification](filesystem_package_specification.md)
### 2. Implementation Process
For each filesystem feature:
1. Write tests following [Testing Guide](testing_guide.md)
2. Implement following Laravel patterns
3. Document following [Getting Started Guide](getting_started.md#documentation)
4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md)
### 3. Quality Requirements
All implementations must:
1. Pass all tests (see [Testing Guide](testing_guide.md))
2. Meet Laravel compatibility requirements
3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md))
4. Match specifications in [FileSystem Package Specification](filesystem_package_specification.md)
### 4. Integration Considerations
When implementing filesystem features:
1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md)
2. Ensure Laravel compatibility per [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Use testing approaches from [Testing Guide](testing_guide.md)
4. Follow development setup in [Getting Started Guide](getting_started.md)
### 5. Performance Guidelines
Filesystem system must:
1. Handle large files efficiently
2. Use streaming where appropriate
3. Minimize memory usage
4. Support concurrent operations
5. Meet performance targets in [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#performance-benchmarks)
### 6. Testing Requirements
Filesystem tests must:
1. Cover all file operations
2. Test streaming behavior
3. Verify cloud storage
4. Check metadata handling
5. Follow patterns in [Testing Guide](testing_guide.md)
### 7. Documentation Requirements
Filesystem documentation must:
1. Explain filesystem patterns
2. Show driver examples
3. Cover error handling
4. Include performance tips
5. Follow standards in [Getting Started Guide](getting_started.md#documentation)

View file

@ -0,0 +1,554 @@
# FileSystem Package Specification
## Overview
The FileSystem package provides a robust abstraction layer for file operations, matching Laravel's filesystem functionality. It supports local and cloud storage systems through a unified API, with support for streaming, visibility control, and metadata management.
> **Related Documentation**
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for implementation status
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Testing Guide](testing_guide.md) for testing approaches
> - See [Getting Started Guide](getting_started.md) for development setup
> - See [Contracts Package Specification](contracts_package_specification.md) for filesystem contracts
## Core Features
### 1. Filesystem Manager
```dart
/// Manages filesystem drivers
class FilesystemManager implements FilesystemFactory {
/// Available filesystem drivers
final Map<String, FilesystemDriver> _drivers = {};
/// Default driver name
final String _defaultDriver;
/// Configuration repository
final ConfigContract _config;
FilesystemManager(this._config)
: _defaultDriver = _config.get('filesystems.default', 'local');
@override
Filesystem disk([String? name]) {
name ??= _defaultDriver;
return _drivers.putIfAbsent(name, () {
var config = _getConfig(name!);
var driver = _createDriver(config);
return Filesystem(driver);
});
}
/// Creates a driver instance
FilesystemDriver _createDriver(Map<String, dynamic> config) {
switch (config['driver']) {
case 'local':
return LocalDriver(config);
case 's3':
return S3Driver(config);
case 'gcs':
return GoogleCloudDriver(config);
default:
throw UnsupportedError(
'Unsupported filesystem driver: ${config['driver']}'
);
}
}
/// Gets configuration for driver
Map<String, dynamic> _getConfig(String name) {
var config = _config.get<Map>('filesystems.disks.$name');
if (config == null) {
throw ArgumentError('Disk [$name] not configured.');
}
return config;
}
}
```
### 2. Filesystem Implementation
```dart
/// Core filesystem implementation
class Filesystem implements FilesystemContract {
/// The filesystem driver
final FilesystemDriver _driver;
Filesystem(this._driver);
@override
Future<bool> exists(String path) {
return _driver.exists(path);
}
@override
Future<String> get(String path) {
return _driver.get(path);
}
@override
Stream<List<int>> readStream(String path) {
return _driver.readStream(path);
}
@override
Future<void> put(String path, dynamic contents, [Map<String, String>? options]) {
return _driver.put(path, contents, options);
}
@override
Future<void> putStream(String path, Stream<List<int>> contents, [Map<String, String>? options]) {
return _driver.putStream(path, contents, options);
}
@override
Future<void> delete(String path) {
return _driver.delete(path);
}
@override
Future<void> copy(String from, String to) {
return _driver.copy(from, to);
}
@override
Future<void> move(String from, String to) {
return _driver.move(from, to);
}
@override
Future<String> url(String path) {
return _driver.url(path);
}
@override
Future<Map<String, String>> metadata(String path) {
return _driver.metadata(path);
}
@override
Future<int> size(String path) {
return _driver.size(path);
}
@override
Future<String> mimeType(String path) {
return _driver.mimeType(path);
}
@override
Future<DateTime> lastModified(String path) {
return _driver.lastModified(path);
}
}
```
### 3. Local Driver
```dart
/// Local filesystem driver
class LocalDriver implements FilesystemDriver {
/// Root path for local filesystem
final String _root;
/// Default visibility
final String _visibility;
LocalDriver(Map<String, dynamic> config)
: _root = config['root'],
_visibility = config['visibility'] ?? 'private';
@override
Future<bool> exists(String path) async {
return File(_fullPath(path)).exists();
}
@override
Future<String> get(String path) async {
return File(_fullPath(path)).readAsString();
}
@override
Stream<List<int>> readStream(String path) {
return File(_fullPath(path)).openRead();
}
@override
Future<void> put(String path, dynamic contents, [Map<String, String>? options]) async {
var file = File(_fullPath(path));
await file.create(recursive: true);
if (contents is String) {
await file.writeAsString(contents);
} else if (contents is List<int>) {
await file.writeAsBytes(contents);
} else {
throw ArgumentError('Invalid content type');
}
await _setVisibility(file, options?['visibility'] ?? _visibility);
}
@override
Future<void> putStream(String path, Stream<List<int>> contents, [Map<String, String>? options]) async {
var file = File(_fullPath(path));
await file.create(recursive: true);
var sink = file.openWrite();
await contents.pipe(sink);
await sink.close();
await _setVisibility(file, options?['visibility'] ?? _visibility);
}
/// Gets full path for file
String _fullPath(String path) {
return p.join(_root, path);
}
/// Sets file visibility
Future<void> _setVisibility(File file, String visibility) async {
// Set file permissions based on visibility
if (visibility == 'public') {
await file.setPermissions(
unix: 0644,
windows: FilePermissions.readWrite
);
} else {
await file.setPermissions(
unix: 0600,
windows: FilePermissions.readWriteExecute
);
}
}
}
```
### 4. Cloud Drivers
```dart
/// Amazon S3 driver
class S3Driver implements FilesystemDriver {
/// S3 client
final S3Client _client;
/// Bucket name
final String _bucket;
/// Optional path prefix
final String? _prefix;
S3Driver(Map<String, dynamic> config)
: _client = S3Client(
region: config['region'],
credentials: AWSCredentials(
accessKey: config['key'],
secretKey: config['secret']
)
),
_bucket = config['bucket'],
_prefix = config['prefix'];
@override
Future<bool> exists(String path) async {
try {
await _client.headObject(
bucket: _bucket,
key: _prefixPath(path)
);
return true;
} catch (e) {
return false;
}
}
@override
Future<void> put(String path, dynamic contents, [Map<String, String>? options]) async {
await _client.putObject(
bucket: _bucket,
key: _prefixPath(path),
body: contents,
acl: options?['visibility'] == 'public'
? 'public-read'
: 'private'
);
}
/// Adds prefix to path
String _prefixPath(String path) {
return _prefix != null ? '$_prefix/$path' : path;
}
}
/// Google Cloud Storage driver
class GoogleCloudDriver implements FilesystemDriver {
/// Storage client
final Storage _storage;
/// Bucket name
final String _bucket;
GoogleCloudDriver(Map<String, dynamic> config)
: _storage = Storage(
projectId: config['project_id'],
credentials: config['credentials']
),
_bucket = config['bucket'];
@override
Future<bool> exists(String path) async {
try {
await _storage.bucket(_bucket).file(path).exists();
return true;
} catch (e) {
return false;
}
}
@override
Future<void> put(String path, dynamic contents, [Map<String, String>? options]) async {
var file = _storage.bucket(_bucket).file(path);
if (contents is String) {
await file.writeAsString(contents);
} else if (contents is List<int>) {
await file.writeAsBytes(contents);
} else {
throw ArgumentError('Invalid content type');
}
if (options?['visibility'] == 'public') {
await file.makePublic();
}
}
}
```
## Integration with Container
```dart
/// Registers filesystem services
class FilesystemServiceProvider extends ServiceProvider {
@override
void register() {
// Register filesystem factory
container.singleton<FilesystemFactory>((c) {
return FilesystemManager(c.make<ConfigContract>());
});
// Register default filesystem
container.singleton<FilesystemContract>((c) {
return c.make<FilesystemFactory>().disk();
});
}
}
```
## Usage Examples
### Basic File Operations
```dart
// Get default disk
var storage = Storage.disk();
// Check if file exists
if (await storage.exists('file.txt')) {
// Read file contents
var contents = await storage.get('file.txt');
// Write file contents
await storage.put('new-file.txt', contents);
// Delete file
await storage.delete('file.txt');
}
```
### Stream Operations
```dart
// Read file as stream
var stream = storage.readStream('large-file.txt');
// Write stream to file
await storage.putStream(
'output.txt',
stream,
{'visibility': 'public'}
);
```
### Cloud Storage
```dart
// Use S3 disk
var s3 = Storage.disk('s3');
// Upload file
await s3.put(
'uploads/image.jpg',
imageBytes,
{'visibility': 'public'}
);
// Get public URL
var url = await s3.url('uploads/image.jpg');
```
### File Metadata
```dart
// Get file metadata
var meta = await storage.metadata('document.pdf');
print('Size: ${meta['size']}');
print('Type: ${meta['mime_type']}');
print('Modified: ${meta['last_modified']}');
```
## Testing
```dart
void main() {
group('Filesystem Tests', () {
late Filesystem storage;
setUp(() {
storage = Filesystem(MockDriver());
});
test('should check file existence', () async {
expect(await storage.exists('test.txt'), isTrue);
expect(await storage.exists('missing.txt'), isFalse);
});
test('should read and write files', () async {
await storage.put('test.txt', 'contents');
var contents = await storage.get('test.txt');
expect(contents, equals('contents'));
});
test('should handle streams', () async {
var input = Stream.fromIterable([
[1, 2, 3],
[4, 5, 6]
]);
await storage.putStream('test.bin', input);
var output = storage.readStream('test.bin');
expect(
await output.toList(),
equals([[1, 2, 3], [4, 5, 6]])
);
});
});
}
```
## Performance Considerations
1. **Streaming Large Files**
```dart
// Use streams for large files
class Filesystem {
Future<void> copyLarge(String from, String to) async {
await readStream(from)
.pipe(writeStream(to));
}
}
```
2. **Caching URLs**
```dart
class CachingFilesystem implements FilesystemContract {
final Cache _cache;
final Duration _ttl;
@override
Future<String> url(String path) async {
var key = 'file_url:$path';
return _cache.remember(key, _ttl, () {
return _driver.url(path);
});
}
}
```
3. **Batch Operations**
```dart
class Filesystem {
Future<void> putMany(Map<String, dynamic> files) async {
await Future.wait(
files.entries.map((e) =>
put(e.key, e.value)
)
);
}
}
```
## Next Steps
1. Implement core filesystem
2. Add local driver
3. Add cloud drivers
4. Create manager
5. Write tests
6. Add benchmarks
Would you like me to focus on implementing any specific part of these packages or continue with other documentation?
## Development Guidelines
### 1. Getting Started
Before implementing filesystem features:
1. Review [Getting Started Guide](getting_started.md)
2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Follow [Testing Guide](testing_guide.md)
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
5. Understand [Contracts Package Specification](contracts_package_specification.md)
### 2. Implementation Process
For each filesystem feature:
1. Write tests following [Testing Guide](testing_guide.md)
2. Implement following Laravel patterns
3. Document following [Getting Started Guide](getting_started.md#documentation)
4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md)
### 3. Quality Requirements
All implementations must:
1. Pass all tests (see [Testing Guide](testing_guide.md))
2. Meet Laravel compatibility requirements
3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md))
4. Implement required contracts (see [Contracts Package Specification](contracts_package_specification.md))
### 4. Integration Considerations
When implementing filesystem features:
1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md)
2. Ensure Laravel compatibility per [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Use testing approaches from [Testing Guide](testing_guide.md)
4. Follow development setup in [Getting Started Guide](getting_started.md)
5. Implement all contracts from [Contracts Package Specification](contracts_package_specification.md)
### 5. Performance Guidelines
Filesystem system must:
1. Handle large files efficiently
2. Use streaming where appropriate
3. Minimize memory usage
4. Support concurrent operations
5. Meet performance targets in [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#performance-benchmarks)
### 6. Testing Requirements
Filesystem tests must:
1. Cover all file operations
2. Test streaming behavior
3. Verify cloud storage
4. Check metadata handling
5. Follow patterns in [Testing Guide](testing_guide.md)
### 7. Documentation Requirements
Filesystem documentation must:
1. Explain filesystem patterns
2. Show driver examples
3. Cover error handling
4. Include performance tips
5. Follow standards in [Getting Started Guide](getting_started.md#documentation)

View file

@ -0,0 +1,316 @@
# Foundation Integration Guide
## Overview
This guide demonstrates how Level 0 and Level 1 packages work together to provide the foundation for the framework. It includes implementation priorities, integration patterns, and best practices.
## Implementation Timeline
### Phase 1: Core Foundation (Level 0)
#### Week 1: Contracts Package
```dart
Priority: Highest
Dependencies: None
Steps:
1. Define core interfaces
2. Create base exceptions
3. Add documentation
4. Write interface tests
```
#### Week 2: Support Package
```dart
Priority: Highest
Dependencies: Contracts
Steps:
1. Implement collections
2. Add string helpers
3. Create service provider base
4. Add utility functions
```
#### Weeks 3-4: Container Package
```dart
Priority: Highest
Dependencies: Contracts, Support
Steps:
1. Implement core container
2. Add contextual binding
3. Add method injection
4. Add tagged bindings
5. Implement caching
```
#### Week 5: Pipeline Package
```dart
Priority: High
Dependencies: Contracts, Support, Container
Steps:
1. Implement core pipeline
2. Add pipeline hub
3. Create middleware support
4. Add async handling
```
### Phase 2: Infrastructure (Level 1)
#### Weeks 6-7: Events Package
```dart
Priority: High
Dependencies: All Level 0
Steps:
1. Implement event dispatcher
2. Add event discovery
3. Create subscriber support
4. Add queueing integration
```
#### Week 8: Config Package
```dart
Priority: High
Dependencies: All Level 0
Steps:
1. Implement config repository
2. Add environment loading
3. Create config caching
4. Add array casting
```
#### Weeks 9-10: FileSystem Package
```dart
Priority: High
Dependencies: All Level 0
Steps:
1. Implement filesystem manager
2. Create local driver
3. Add cloud drivers
4. Implement streaming
```
## Integration Examples
### 1. Service Provider Integration
```dart
/// Example showing how packages integrate through service providers
void main() {
var container = Container();
// Register foundation services
container.register(SupportServiceProvider());
container.register(PipelineServiceProvider());
container.register(EventServiceProvider());
container.register(ConfigServiceProvider());
container.register(FilesystemServiceProvider());
// Boot application
await container.bootProviders();
}
```
### 2. Event-Driven File Operations
```dart
/// Example showing Events and FileSystem integration
class FileUploadHandler {
final EventDispatcherContract _events;
final FilesystemContract _storage;
Future<void> handleUpload(Upload upload) async {
// Store file using FileSystem
await _storage.put(
'uploads/${upload.filename}',
upload.contents,
{'visibility': 'public'}
);
// Dispatch event using Events
await _events.dispatch(FileUploaded(
filename: upload.filename,
size: upload.size,
url: await _storage.url('uploads/${upload.filename}')
));
}
}
```
### 3. Configuration-Based Pipeline
```dart
/// Example showing Config and Pipeline integration
class RequestHandler {
final ConfigContract _config;
final Pipeline<Request> _pipeline;
Future<Response> handle(Request request) async {
// Get middleware from config
var middleware = _config.get<List>('http.middleware', [])
.map((m) => container.make<Middleware>(m))
.toList();
// Process request through pipeline
return _pipeline
.through(middleware)
.send(request)
.then((request) => processRequest(request));
}
}
```
## Common Integration Patterns
### 1. Service Provider Pattern
```dart
abstract class ServiceProvider {
void register() {
container.singleton<Service>((c) =>
ServiceImpl(
c.make<EventDispatcherContract>(),
c.make<ConfigContract>(),
c.make<FilesystemContract>()
)
);
}
}
```
### 2. Event-Driven Pattern
```dart
class EventDrivenService {
final EventDispatcherContract events;
void initialize() {
events.listen<ConfigurationChanged>(_handleConfigChange);
events.listen<StorageEvent>(_handleStorageEvent);
}
}
```
### 3. Pipeline Pattern
```dart
class ServicePipeline {
final Pipeline<Request> pipeline;
ServicePipeline(this.pipeline) {
pipeline.through([
ConfigMiddleware(container.make<ConfigContract>()),
EventMiddleware(container.make<EventDispatcherContract>()),
StorageMiddleware(container.make<FilesystemContract>())
]);
}
}
```
## Testing Strategy
### 1. Unit Tests
```dart
void main() {
group('Package Tests', () {
test('core functionality', () {
// Test core features
});
test('integration points', () {
// Test integration with other packages
});
});
}
```
### 2. Integration Tests
```dart
void main() {
group('Integration Tests', () {
late Container container;
setUp(() {
container = Container();
container.register(SupportServiceProvider());
container.register(EventServiceProvider());
});
test('should handle file upload with events', () async {
var handler = container.make<FileUploadHandler>();
var events = container.make<EventDispatcherContract>();
var received = <FileUploaded>[];
events.listen<FileUploaded>((event) {
received.add(event);
});
await handler.handleUpload(testUpload);
expect(received, hasLength(1));
});
});
}
```
## Quality Checklist
### 1. Code Quality
- [ ] Follows style guide
- [ ] Uses static analysis
- [ ] Has documentation
- [ ] Has tests
- [ ] Handles errors
### 2. Package Quality
- [ ] Has README
- [ ] Has examples
- [ ] Has changelog
- [ ] Has license
- [ ] Has CI/CD
### 3. Integration Quality
- [ ] Works with container
- [ ] Supports events
- [ ] Uses configuration
- [ ] Has providers
## Best Practices
1. **Use Service Providers**
```dart
// Register dependencies in providers
class ServiceProvider {
void register() {
// Register all required services
}
}
```
2. **Event-Driven Communication**
```dart
// Use events for cross-package communication
class Service {
final EventDispatcherContract _events;
Future<void> doSomething() async {
await _events.dispatch(SomethingHappened());
}
}
```
3. **Configuration-Based Setup**
```dart
// Use configuration for service setup
class Service {
void initialize(ConfigContract config) {
if (config.get('service.enabled')) {
// Initialize service
}
}
}
```
## Next Steps
1. Follow implementation timeline
2. Review package dependencies
3. Implement integration tests
4. Document common patterns
5. Create example applications
Would you like me to:
1. Start implementing a specific package?
2. Create detailed integration tests?
3. Build example applications?

142
docs/getting_started.md Normal file
View file

@ -0,0 +1,142 @@
# Getting Started Guide
## Overview
This guide helps developers get started with implementing and contributing to the framework's foundation packages. It provides step-by-step instructions for setting up the development environment, understanding the codebase, and making contributions.
## Key Documentation
Before starting, familiarize yourself with our core documentation:
1. **Architecture & Implementation**
- [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) - Overall implementation status and plans
- [Foundation Integration Guide](foundation_integration_guide.md) - How packages work together
- [Testing Guide](testing_guide.md) - Testing approaches and standards
2. **Package Documentation**
- [Container Package](container_package_specification.md) - Dependency injection system
- [Container Gap Analysis](container_gap_analysis.md) - Implementation status and plans
- More package docs coming soon...
3. **Development Setup**
- [Melos Configuration](melos_config.md) - Build and development tools
[Previous content remains the same until Project Structure section, then update with:]
## Project Structure
### 1. Package Organization
```
platform/
├── packages/
│ ├── container/ # Dependency injection
│ │ ├── container/ # Core container
│ │ └── container_generator/ # Code generation
│ ├── core/ # Framework core
│ ├── events/ # Event system
│ ├── model/ # Model system
│ ├── pipeline/ # Pipeline pattern
│ ├── process/ # Process management
│ ├── queue/ # Queue system
│ ├── route/ # Routing system
│ ├── support/ # Utilities
│ └── testing/ # Testing utilities
├── apps/ # Example applications
├── config/ # Configuration files
├── docs/ # Documentation
├── examples/ # Usage examples
├── resources/ # Additional resources
├── scripts/ # Development scripts
├── templates/ # Project templates
└── tests/ # Integration tests
```
### 2. Package Structure
```
package/
├── lib/
│ ├── src/
│ │ ├── core/ # Core implementation
│ │ ├── contracts/ # Package interfaces
│ │ └── support/ # Package utilities
│ └── package.dart # Public API
├── test/
│ ├── unit/ # Unit tests
│ ├── integration/ # Integration tests
│ └── performance/ # Performance tests
├── example/ # Usage examples
└── README.md # Package documentation
```
[Previous content remains the same until Implementation Guidelines section, then update with:]
## Implementation Guidelines
### 1. Laravel Compatibility
```dart
// Follow Laravel patterns where possible
class ServiceProvider {
void register() {
// Register services like Laravel
container.singleton<Service>((c) => ServiceImpl());
// Use contextual binding
container.when(PhotoController)
.needs<Storage>()
.give(LocalStorage());
// Use tagged bindings
container.tag([
EmailNotifier,
SmsNotifier
], 'notifications');
}
}
```
### 2. Testing Approach
```dart
// Follow Laravel testing patterns
void main() {
group('Feature Tests', () {
late TestCase test;
setUp(() {
test = await TestCase.make();
});
test('user can register', () async {
await test
.post('/register', {
'name': 'John Doe',
'email': 'john@example.com',
'password': 'password'
})
.assertStatus(302)
.assertRedirect('/home');
});
});
}
```
[Previous content remains the same until Getting Help section, then update with:]
## Getting Help
1. **Documentation**
- Start with [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
- Review [Foundation Integration Guide](foundation_integration_guide.md)
- Check [Testing Guide](testing_guide.md)
- Read package-specific documentation
2. **Development Setup**
- Follow [Melos Configuration](melos_config.md)
- Setup development environment
- Run example applications
3. **Resources**
- [Laravel Documentation](https://laravel.com/docs)
- [Dart Documentation](https://dart.dev/guides)
- [Package Layout](https://dart.dev/tools/pub/package-layout)
[Rest of the file remains the same]

234
docs/index.md Normal file
View file

@ -0,0 +1,234 @@
# Framework Documentation
## Core Documentation
### Getting Started
1. [Getting Started Guide](getting_started.md)
2. [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. [Foundation Integration Guide](foundation_integration_guide.md)
4. [Testing Guide](testing_guide.md)
5. [Package Integration Map](package_integration_map.md)
### Core Architecture
1. [Core Architecture](core_architecture.md)
- System design
- Architectural patterns
- Extension points
- Package integration
## Package Documentation
### Core Framework
1. Core Package
- [Core Package Specification](core_package_specification.md)
- [Core Architecture](core_architecture.md)
2. Container Package
- [Container Package Specification](container_package_specification.md)
- [Container Gap Analysis](container_gap_analysis.md)
- [Container Feature Integration](container_feature_integration.md)
- [Container Migration Guide](container_migration_guide.md)
3. Contracts Package
- [Contracts Package Specification](contracts_package_specification.md)
4. Events Package
- [Events Package Specification](events_package_specification.md)
- [Events Gap Analysis](events_gap_analysis.md)
5. Pipeline Package
- [Pipeline Package Specification](pipeline_package_specification.md)
- [Pipeline Gap Analysis](pipeline_gap_analysis.md)
6. Support Package
- [Support Package Specification](support_package_specification.md)
### Infrastructure
1. Bus Package
- [Bus Package Specification](bus_package_specification.md)
- [Bus Gap Analysis](bus_gap_analysis.md)
2. Config Package
- [Config Package Specification](config_package_specification.md)
- [Config Gap Analysis](config_gap_analysis.md)
3. Filesystem Package
- [Filesystem Package Specification](filesystem_package_specification.md)
- [Filesystem Gap Analysis](filesystem_gap_analysis.md)
4. Model Package
- [Model Package Specification](model_package_specification.md)
- [Model Gap Analysis](model_gap_analysis.md)
5. Process Package
- [Process Package Specification](process_package_specification.md)
- [Process Gap Analysis](process_gap_analysis.md)
6. Queue Package
- [Queue Package Specification](queue_package_specification.md)
- [Queue Gap Analysis](queue_gap_analysis.md)
7. Route Package
- [Route Package Specification](route_package_specification.md)
- [Route Gap Analysis](route_gap_analysis.md)
8. Testing Package
- [Testing Package Specification](testing_package_specification.md)
- [Testing Gap Analysis](testing_gap_analysis.md)
## Package Dependencies
```mermaid
graph TD
Core[Core] --> Container[Container]
Core --> Events[Events]
Core --> Pipeline[Pipeline]
Container --> Contracts[Contracts]
Events --> Container
Pipeline --> Container
Bus[Bus] --> Events
Bus --> Queue[Queue]
Config[Config] --> Container
Filesystem[Filesystem] --> Container
Model[Model] --> Events
Model --> Container
Process[Process] --> Events
Process --> Queue
Queue --> Events
Queue --> Container
Route[Route] --> Pipeline
Route --> Container
Testing[Testing] --> Container
Testing --> Events
```
## Implementation Status
### Core Framework (90%)
- Core Package (95%)
* Application lifecycle ✓
* Service providers ✓
* HTTP kernel ✓
* Console kernel ✓
* Exception handling ✓
* Needs: Performance optimizations
- Container Package (90%)
* Basic DI ✓
* Auto-wiring ✓
* Service providers ✓
* Needs: Contextual binding
- Events Package (85%)
* Event dispatching ✓
* Event subscribers ✓
* Event broadcasting ✓
* Needs: Event discovery
### Infrastructure (80%)
- Bus Package (85%)
* Command dispatching ✓
* Command queuing ✓
* Needs: Command batching
- Config Package (80%)
* Configuration repository ✓
* Environment loading ✓
* Needs: Config caching
- Filesystem Package (75%)
* Local driver ✓
* Cloud storage ✓
* Needs: Streaming support
- Model Package (80%)
* Basic ORM ✓
* Relationships ✓
* Needs: Model events
- Process Package (85%)
* Process management ✓
* Process pools ✓
* Needs: Process monitoring
- Queue Package (85%)
* Queue workers ✓
* Job batching ✓
* Needs: Rate limiting
- Route Package (90%)
* Route registration ✓
* Route matching ✓
* Middleware ✓
* Needs: Route caching
- Testing Package (85%)
* HTTP testing ✓
* Database testing ✓
* Needs: Browser testing
## Development Workflow
1. **Starting Development**
```bash
# Clone repository
git clone https://github.com/organization/framework.git
# Install dependencies
dart pub get
# Run tests
dart test
```
2. **Development Process**
- Write tests first
- Implement features
- Update documentation
- Submit PR
3. **Quality Checks**
- Run tests
- Check code style
- Verify documentation
- Review performance
## Contributing
See [CONTRIBUTING.md](../CONTRIBUTING.md) for detailed contribution guidelines.
### Quick Start
1. Review [Getting Started Guide](getting_started.md)
2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Read relevant package documentation
4. Follow [Testing Guide](testing_guide.md)
## Resources
### Documentation
- [Laravel Documentation](https://laravel.com/docs)
- [Dart Documentation](https://dart.dev/guides)
- [Package Layout](https://dart.dev/tools/pub/package-layout)
### Tools
- [Dart SDK](https://dart.dev/get-dart)
- [VS Code](https://code.visualstudio.com)
- [Git](https://git-scm.com)
### Community
- GitHub Issues
- Discussion Forum
- Team Chat
## License
This framework is open-sourced software licensed under the [MIT license](../LICENSE).

View file

@ -0,0 +1,251 @@
# Laravel Compatibility Roadmap
## Overview
This document outlines our path to Laravel API compatibility while maintaining backward compatibility with existing code. It provides a comprehensive view of package dependencies, implementation status, and migration strategy.
## Package Dependency Hierarchy
### Level 0: Core Foundation
```mermaid
graph TD
Container[Container] --> Contracts[Contracts]
Support[Support] --> Container
Pipeline[Pipeline] --> Container
```
Core Dependencies:
- Container: Service container, dependency injection
- Contracts: Interfaces and contracts
- Support: Helper functions, utilities
- Pipeline: Pipeline pattern implementation
### Level 1: Infrastructure
```mermaid
graph TD
Events[Events] --> Container
Events --> Support
Config[Config] --> Container
Config --> Support
FileSystem[FileSystem] --> Support
FileSystem --> Container
```
Infrastructure Dependencies:
- Events: Event dispatching system
- Config: Configuration management
- FileSystem: File system abstraction
### Level 2: Core Services
```mermaid
graph TD
Cache[Cache] --> Events
Cache --> Container
Database[Database] --> Events
Database --> Container
Queue[Queue] --> Events
Queue --> Container
Queue --> Pipeline
```
Core Service Dependencies:
- Cache: Caching system
- Database: Database abstraction
- Queue: Queue system and job processing
### Level 3: HTTP Layer
```mermaid
graph TD
Routing[Routing] --> Pipeline
Routing --> Container
Http[Http] --> Pipeline
Http --> Events
Session[Session] --> Cache
Session --> Events
```
HTTP Layer Dependencies:
- Routing: Route registration and matching
- Http: HTTP request/response handling
- Session: Session management
## Current Implementation Status
[Previous implementation status section remains the same]
## Success Metrics
### 1. API Compatibility
```yaml
Required:
- 100% Laravel interface implementation
- All Laravel patterns supported
- Full feature parity
- Backward compatibility maintained
```
### 2. Performance
```yaml
Targets:
- Resolution: < 0.1ms per operation
- Memory: < 10MB overhead
- Cache hit rate: > 90%
- Startup time: < 100ms
```
### 3. Code Quality
```yaml
Requirements:
- 100% test coverage
- Static analysis passing
- Documentation complete
- Examples provided
```
### 4. Integration
```yaml
Verification:
- Cross-package tests passing
- Performance benchmarks met
- Real-world examples working
- Migration guides verified
```
## Key Design Decisions
### 1. Backward Compatibility
```dart
// Maintain existing APIs
class Container {
// Existing methods stay the same
T make<T>();
void bind<T>(T instance);
// New methods add functionality
ContextualBindingBuilder when(Type concrete);
void tag(List<Type> types, String tag);
}
```
### 2. Laravel Compatibility
```dart
// Match Laravel's patterns
container.when(UserController)
.needs<Service>()
.give((c) => SpecialService());
container.tag([ServiceA, ServiceB], 'services');
container.call(instance, 'method', parameters);
```
### 3. Performance Focus
```dart
// Add caching
class Container {
final ResolutionCache _cache;
final ReflectionCache _reflectionCache;
T make<T>([dynamic context]) {
return _cache.get<T>(context) ?? _resolve<T>(context);
}
}
```
## Implementation Strategy
[Previous implementation strategy section remains the same]
## Integration Considerations
### 1. Service Provider Pattern
- Registration phase
- Boot phase
- Deferred providers
### 2. Event System
- Synchronous events
- Queued events
- Event subscribers
### 3. Queue System
- Multiple drivers
- Job handling
- Failed jobs
### 4. Database Layer
- Query builder
- Schema builder
- Migrations
### 5. HTTP Layer
- Middleware
- Controllers
- Resources
### 6. Authentication
- Guards
- Providers
- Policies
## Getting Started
### 1. Development Environment
```bash
# Clone repository
git clone https://github.com/org/platform.git
# Install dependencies
dart pub get
# Run tests
dart test
```
### 2. Package Development
```yaml
1. Choose package level:
- Level 0: Foundation packages
- Level 1: Infrastructure packages
- Level 2: Core services
- Level 3: HTTP layer
2. Review dependencies:
- Check required packages
- Verify integration points
- Plan implementation
3. Follow implementation order:
- Core functionality
- Laravel compatibility
- Tests and documentation
```
### 3. Quality Assurance
```yaml
1. Testing:
- Unit tests
- Integration tests
- Performance tests
- Compatibility tests
2. Documentation:
- API documentation
- Usage examples
- Integration guides
- Migration guides
3. Performance:
- Benchmarking
- Profiling
- Optimization
```
## Next Steps
[Previous next steps section remains the same]
Would you like me to:
1. Create detailed plans for package creation?
2. Start implementing specific features?
3. Create test plans for new functionality?

316
docs/model_gap_analysis.md Normal file
View file

@ -0,0 +1,316 @@
# Model Package Gap Analysis
## Overview
This document analyzes the gaps between our Model package's actual implementation and Laravel's Eloquent functionality, identifying areas that need implementation or documentation updates.
> **Related Documentation**
> - See [Model Package Specification](model_package_specification.md) for current implementation
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for overall status
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Testing Guide](testing_guide.md) for testing approaches
> - See [Getting Started Guide](getting_started.md) for development setup
## Implementation Gaps
### 1. Missing Laravel Features
```dart
// Documented but not implemented:
// 1. Model Scopes
class ModelScope {
// Need to implement:
Query<T> apply<T extends Model>(Query<T> query);
bool shouldApply<T extends Model>(Query<T> query);
}
// 2. Model Observers
class ModelObserver<T extends Model> {
// Need to implement:
void creating(T model);
void created(T model);
void updating(T model);
void updated(T model);
void deleting(T model);
void deleted(T model);
void restored(T model);
void forceDeleted(T model);
}
// 3. Model Factories
class ModelFactory<T extends Model> {
// Need to implement:
T definition();
T make([Map<String, dynamic>? attributes]);
Future<T> create([Map<String, dynamic>? attributes]);
List<T> makeMany(int count, [Map<String, dynamic>? attributes]);
Future<List<T>> createMany(int count, [Map<String, dynamic>? attributes]);
}
```
### 2. Missing Relationship Types
```dart
// Need to implement:
// 1. Many to Many
class BelongsToMany<T extends Model> extends Relationship<T> {
// Need to implement:
String get table;
String get foreignPivotKey;
String get relatedPivotKey;
List<String> get pivotColumns;
Future<List<T>> get();
Future<void> attach(List<dynamic> ids, [Map<String, dynamic>? attributes]);
Future<void> detach(List<dynamic>? ids);
Future<void> sync(List<dynamic> ids);
Future<void> toggle(List<dynamic> ids);
Future<void> updateExistingPivot(dynamic id, Map<String, dynamic> attributes);
}
// 2. Has Many Through
class HasManyThrough<T extends Model> extends Relationship<T> {
// Need to implement:
String get through;
String get firstKey;
String get secondKey;
String get localKey;
String get secondLocalKey;
Future<List<T>> get();
}
// 3. Polymorphic Relations
class MorphTo extends Relationship<Model> {
// Need to implement:
String get morphType;
String get morphId;
Future<Model?> get();
}
```
### 3. Missing Query Features
```dart
// Need to implement:
// 1. Advanced Where Clauses
class Query<T extends Model> {
// Need to implement:
Query<T> whereIn(String column, List<dynamic> values);
Query<T> whereNotIn(String column, List<dynamic> values);
Query<T> whereBetween(String column, List<dynamic> values);
Query<T> whereNotBetween(String column, List<dynamic> values);
Query<T> whereNull(String column);
Query<T> whereNotNull(String column);
Query<T> whereDate(String column, DateTime date);
Query<T> whereMonth(String column, int month);
Query<T> whereYear(String column, int year);
Query<T> whereTime(String column, String operator, DateTime time);
}
// 2. Joins
class Query<T extends Model> {
// Need to implement:
Query<T> join(String table, String first, [String? operator, String? second]);
Query<T> leftJoin(String table, String first, [String? operator, String? second]);
Query<T> rightJoin(String table, String first, [String? operator, String? second]);
Query<T> crossJoin(String table);
}
// 3. Aggregates
class Query<T extends Model> {
// Need to implement:
Future<int> count([String column = '*']);
Future<dynamic> max(String column);
Future<dynamic> min(String column);
Future<num> avg(String column);
Future<num> sum(String column);
}
```
## Documentation Gaps
### 1. Missing API Documentation
```dart
// Need to document:
/// Applies a scope to the query.
///
/// Example:
/// ```dart
/// class PublishedScope implements Scope {
/// Query<T> apply<T extends Model>(Query<T> query) {
/// return query.where('published', true);
/// }
/// }
/// ```
void addGlobalScope(Scope scope);
/// Defines a local scope.
///
/// Example:
/// ```dart
/// Query<Post> published() {
/// return where('published', true);
/// }
/// ```
void scopePublished(Query query);
```
### 2. Missing Integration Examples
```dart
// Need examples for:
// 1. Model Observers
class UserObserver extends ModelObserver<User> {
@override
void created(User user) {
// Send welcome email
}
@override
void deleted(User user) {
// Cleanup user data
}
}
// 2. Model Factories
class UserFactory extends ModelFactory<User> {
@override
User definition() {
return User()
..name = faker.person.name()
..email = faker.internet.email();
}
}
// 3. Many to Many Relationships
class User extends Model {
Future<List<Role>> roles() {
return belongsToMany<Role>('role_user')
.withPivot(['expires_at'])
.wherePivot('active', true)
.get();
}
}
```
### 3. Missing Test Coverage
```dart
// Need tests for:
void main() {
group('Model Scopes', () {
test('applies global scopes', () async {
var posts = await Post.all();
expect(posts.every((p) => p.published), isTrue);
});
test('applies local scopes', () async {
var posts = await Post().recent().popular().get();
expect(posts, hasLength(greaterThan(0)));
});
});
group('Model Factories', () {
test('creates model instances', () async {
var users = await UserFactory().createMany(3);
expect(users, hasLength(3));
expect(users.first.name, isNotEmpty);
});
});
}
```
## Implementation Priority
1. **High Priority**
- Model scopes (Laravel compatibility)
- Model observers (Laravel compatibility)
- Many to Many relationships
2. **Medium Priority**
- Model factories
- Advanced where clauses
- Query joins
3. **Low Priority**
- Additional relationship types
- Additional query features
- Performance optimizations
## Next Steps
1. **Implementation Tasks**
- Add model scopes
- Add model observers
- Add many to many relationships
- Add model factories
2. **Documentation Tasks**
- Document model scopes
- Document model observers
- Document relationships
- Add integration examples
3. **Testing Tasks**
- Add scope tests
- Add observer tests
- Add relationship tests
- Add factory tests
## Development Guidelines
### 1. Getting Started
Before implementing model features:
1. Review [Getting Started Guide](getting_started.md)
2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Follow [Testing Guide](testing_guide.md)
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
5. Review [Model Package Specification](model_package_specification.md)
### 2. Implementation Process
For each model feature:
1. Write tests following [Testing Guide](testing_guide.md)
2. Implement following Laravel patterns
3. Document following [Getting Started Guide](getting_started.md#documentation)
4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md)
### 3. Quality Requirements
All implementations must:
1. Pass all tests (see [Testing Guide](testing_guide.md))
2. Meet Laravel compatibility requirements
3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md))
4. Match specifications in [Model Package Specification](model_package_specification.md)
### 4. Integration Considerations
When implementing model features:
1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md)
2. Ensure Laravel compatibility per [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Use testing approaches from [Testing Guide](testing_guide.md)
4. Follow development setup in [Getting Started Guide](getting_started.md)
### 5. Performance Guidelines
Model system must:
1. Handle large datasets efficiently
2. Optimize relationship loading
3. Support eager loading
4. Cache query results
5. Meet performance targets in [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#performance-benchmarks)
### 6. Testing Requirements
Model tests must:
1. Cover all model operations
2. Test relationships
3. Verify events
4. Check query building
5. Follow patterns in [Testing Guide](testing_guide.md)
### 7. Documentation Requirements
Model documentation must:
1. Explain model patterns
2. Show relationship examples
3. Cover event handling
4. Include performance tips
5. Follow standards in [Getting Started Guide](getting_started.md#documentation)

View file

@ -0,0 +1,486 @@
# Model Package Specification
## Overview
The Model package provides a robust data modeling system that matches Laravel's Eloquent functionality. It supports active record pattern, relationships, attribute casting, serialization, and model events while leveraging Dart's type system.
> **Related Documentation**
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for implementation status
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Testing Guide](testing_guide.md) for testing approaches
> - See [Getting Started Guide](getting_started.md) for development setup
> - See [Events Package Specification](events_package_specification.md) for model events
## Core Features
### 1. Base Model
```dart
/// Core model implementation
abstract class Model {
/// Model attributes
final Map<String, dynamic> _attributes = {};
/// Original attributes
final Map<String, dynamic> _original = {};
/// Changed attributes
final Set<String> _changes = {};
/// Model constructor
Model([Map<String, dynamic>? attributes]) {
fill(attributes ?? {});
}
/// Gets table name
String get table;
/// Gets primary key
String get primaryKey => 'id';
/// Gets fillable attributes
List<String> get fillable => [];
/// Gets guarded attributes
List<String> get guarded => ['id'];
/// Gets attribute value
dynamic operator [](String key) => getAttribute(key);
/// Sets attribute value
operator []=(String key, dynamic value) => setAttribute(key, value);
/// Gets an attribute
dynamic getAttribute(String key) {
return _attributes[key];
}
/// Sets an attribute
void setAttribute(String key, dynamic value) {
if (!_original.containsKey(key)) {
_original[key] = _attributes[key];
}
_attributes[key] = value;
_changes.add(key);
}
/// Fills attributes
void fill(Map<String, dynamic> attributes) {
for (var key in attributes.keys) {
if (_isFillable(key)) {
this[key] = attributes[key];
}
}
}
/// Checks if attribute is fillable
bool _isFillable(String key) {
if (guarded.contains(key)) return false;
if (fillable.isEmpty) return true;
return fillable.contains(key);
}
/// Gets changed attributes
Map<String, dynamic> getDirty() {
var dirty = <String, dynamic>{};
for (var key in _changes) {
dirty[key] = _attributes[key];
}
return dirty;
}
/// Checks if model is dirty
bool get isDirty => _changes.isNotEmpty;
/// Gets original attributes
Map<String, dynamic> getOriginal() => Map.from(_original);
/// Resets changes
void syncOriginal() {
_original.clear();
_original.addAll(_attributes);
_changes.clear();
}
/// Converts to map
Map<String, dynamic> toMap() => Map.from(_attributes);
/// Converts to JSON
String toJson() => jsonEncode(toMap());
}
```
### 2. Model Relationships
```dart
/// Has one relationship
class HasOne<T extends Model> extends Relationship<T> {
/// Foreign key
final String foreignKey;
/// Local key
final String localKey;
HasOne(Query<T> query, Model parent, this.foreignKey, this.localKey)
: super(query, parent);
@override
Future<T?> get() async {
return await query
.where(foreignKey, parent[localKey])
.first();
}
}
/// Has many relationship
class HasMany<T extends Model> extends Relationship<T> {
/// Foreign key
final String foreignKey;
/// Local key
final String localKey;
HasMany(Query<T> query, Model parent, this.foreignKey, this.localKey)
: super(query, parent);
@override
Future<List<T>> get() async {
return await query
.where(foreignKey, parent[localKey])
.get();
}
}
/// Belongs to relationship
class BelongsTo<T extends Model> extends Relationship<T> {
/// Foreign key
final String foreignKey;
/// Owner key
final String ownerKey;
BelongsTo(Query<T> query, Model child, this.foreignKey, this.ownerKey)
: super(query, child);
@override
Future<T?> get() async {
return await query
.where(ownerKey, parent[foreignKey])
.first();
}
}
```
### 3. Model Events
```dart
/// Model events mixin
mixin ModelEvents {
/// Event dispatcher
static EventDispatcherContract? _dispatcher;
/// Sets event dispatcher
static void setEventDispatcher(EventDispatcherContract dispatcher) {
_dispatcher = dispatcher;
}
/// Fires a model event
Future<bool> fireModelEvent(String event) async {
if (_dispatcher == null) return true;
var result = await _dispatcher!.dispatch('model.$event', this);
return result != false;
}
/// Fires creating event
Future<bool> fireCreatingEvent() => fireModelEvent('creating');
/// Fires created event
Future<bool> fireCreatedEvent() => fireModelEvent('created');
/// Fires updating event
Future<bool> fireUpdatingEvent() => fireModelEvent('updating');
/// Fires updated event
Future<bool> fireUpdatedEvent() => fireModelEvent('updated');
/// Fires deleting event
Future<bool> fireDeletingEvent() => fireModelEvent('deleting');
/// Fires deleted event
Future<bool> fireDeletedEvent() => fireModelEvent('deleted');
}
```
### 4. Model Query Builder
```dart
/// Model query builder
class Query<T extends Model> {
/// Database connection
final DatabaseConnection _connection;
/// Model instance
final T _model;
/// Query constraints
final List<String> _wheres = [];
final List<dynamic> _bindings = [];
final List<String> _orders = [];
int? _limit;
int? _offset;
Query(this._connection, this._model);
/// Adds where clause
Query<T> where(String column, [dynamic value]) {
_wheres.add('$column = ?');
_bindings.add(value);
return this;
}
/// Adds order by clause
Query<T> orderBy(String column, [String direction = 'asc']) {
_orders.add('$column $direction');
return this;
}
/// Sets limit
Query<T> limit(int limit) {
_limit = limit;
return this;
}
/// Sets offset
Query<T> offset(int offset) {
_offset = offset;
return this;
}
/// Gets first result
Future<T?> first() async {
var results = await get();
return results.isEmpty ? null : results.first;
}
/// Gets results
Future<List<T>> get() async {
var sql = _toSql();
var rows = await _connection.select(sql, _bindings);
return rows.map((row) => _hydrate(row)).toList();
}
/// Builds SQL query
String _toSql() {
var sql = 'select * from ${_model.table}';
if (_wheres.isNotEmpty) {
sql += ' where ${_wheres.join(' and ')}';
}
if (_orders.isNotEmpty) {
sql += ' order by ${_orders.join(', ')}';
}
if (_limit != null) {
sql += ' limit $_limit';
}
if (_offset != null) {
sql += ' offset $_offset';
}
return sql;
}
/// Hydrates model from row
T _hydrate(Map<String, dynamic> row) {
var instance = _model.newInstance() as T;
instance.fill(row);
instance.syncOriginal();
return instance;
}
}
```
## Integration Examples
### 1. Basic Model Usage
```dart
// Define model
class User extends Model {
@override
String get table => 'users';
@override
List<String> get fillable => ['name', 'email'];
String get name => this['name'];
set name(String value) => this['name'] = value;
String get email => this['email'];
set email(String value) => this['email'] = value;
}
// Create user
var user = User()
..name = 'John Doe'
..email = 'john@example.com';
await user.save();
// Find user
var found = await User.find(1);
print(found.name); // John Doe
```
### 2. Relationships
```dart
class User extends Model {
// Has many posts
Future<List<Post>> posts() {
return hasMany<Post>('user_id').get();
}
// Has one profile
Future<Profile?> profile() {
return hasOne<Profile>('user_id').get();
}
}
class Post extends Model {
// Belongs to user
Future<User?> user() {
return belongsTo<User>('user_id').get();
}
}
// Use relationships
var user = await User.find(1);
var posts = await user.posts();
var profile = await user.profile();
```
### 3. Events
```dart
// Register event listener
Model.getEventDispatcher().listen<ModelCreated<User>>((event) {
var user = event.model;
print('User ${user.name} was created');
});
// Create user (triggers event)
var user = User()
..name = 'Jane Doe'
..email = 'jane@example.com';
await user.save();
```
## Testing
```dart
void main() {
group('Model', () {
test('handles attributes', () {
var user = User()
..name = 'John'
..email = 'john@example.com';
expect(user.name, equals('John'));
expect(user.isDirty, isTrue);
expect(user.getDirty(), containsPair('name', 'John'));
});
test('tracks changes', () {
var user = User()
..fill({
'name': 'John',
'email': 'john@example.com'
});
user.syncOriginal();
user.name = 'Jane';
expect(user.isDirty, isTrue);
expect(user.getOriginal()['name'], equals('John'));
expect(user.name, equals('Jane'));
});
});
group('Relationships', () {
test('loads relationships', () async {
var user = await User.find(1);
var posts = await user.posts();
expect(posts, hasLength(greaterThan(0)));
expect(posts.first, isA<Post>());
});
});
}
```
## Next Steps
1. Implement core model features
2. Add relationship types
3. Add model events
4. Add query builder
5. Write tests
6. Add benchmarks
## Development Guidelines
### 1. Getting Started
Before implementing model features:
1. Review [Getting Started Guide](getting_started.md)
2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Follow [Testing Guide](testing_guide.md)
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
5. Review [Events Package Specification](events_package_specification.md)
### 2. Implementation Process
For each model feature:
1. Write tests following [Testing Guide](testing_guide.md)
2. Implement following Laravel patterns
3. Document following [Getting Started Guide](getting_started.md#documentation)
4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md)
### 3. Quality Requirements
All implementations must:
1. Pass all tests (see [Testing Guide](testing_guide.md))
2. Meet Laravel compatibility requirements
3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md))
4. Support model events (see [Events Package Specification](events_package_specification.md))
### 4. Integration Considerations
When implementing model features:
1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md)
2. Ensure Laravel compatibility per [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Use testing approaches from [Testing Guide](testing_guide.md)
4. Follow development setup in [Getting Started Guide](getting_started.md)
### 5. Performance Guidelines
Model system must:
1. Handle large datasets efficiently
2. Optimize relationship loading
3. Support eager loading
4. Cache query results
5. Meet performance targets in [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#performance-benchmarks)
### 6. Testing Requirements
Model tests must:
1. Cover all model operations
2. Test relationships
3. Verify events
4. Check query building
5. Follow patterns in [Testing Guide](testing_guide.md)
### 7. Documentation Requirements
Model documentation must:
1. Explain model patterns
2. Show relationship examples
3. Cover event handling
4. Include performance tips
5. Follow standards in [Getting Started Guide](getting_started.md#documentation)

View file

@ -0,0 +1,540 @@
# Package Integration Map
## Overview
This document maps out the integration points between our framework packages and outlines how to maintain and enhance these integrations while achieving Laravel API compatibility.
> **Related Documentation**
> - See [Core Architecture](core_architecture.md) for system design
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for implementation status
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Testing Guide](testing_guide.md) for testing approaches
## Package Documentation
### Core Framework
1. Core Package
- [Core Package Specification](core_package_specification.md)
- [Core Architecture](core_architecture.md)
2. Container Package
- [Container Package Specification](container_package_specification.md)
- [Container Gap Analysis](container_gap_analysis.md)
- [Container Feature Integration](container_feature_integration.md)
- [Container Migration Guide](container_migration_guide.md)
3. Contracts Package
- [Contracts Package Specification](contracts_package_specification.md)
4. Events Package
- [Events Package Specification](events_package_specification.md)
- [Events Gap Analysis](events_gap_analysis.md)
5. Pipeline Package
- [Pipeline Package Specification](pipeline_package_specification.md)
- [Pipeline Gap Analysis](pipeline_gap_analysis.md)
6. Support Package
- [Support Package Specification](support_package_specification.md)
### Infrastructure
1. Bus Package
- [Bus Package Specification](bus_package_specification.md)
- [Bus Gap Analysis](bus_gap_analysis.md)
2. Config Package
- [Config Package Specification](config_package_specification.md)
- [Config Gap Analysis](config_gap_analysis.md)
3. Filesystem Package
- [Filesystem Package Specification](filesystem_package_specification.md)
- [Filesystem Gap Analysis](filesystem_gap_analysis.md)
4. Model Package
- [Model Package Specification](model_package_specification.md)
- [Model Gap Analysis](model_gap_analysis.md)
5. Process Package
- [Process Package Specification](process_package_specification.md)
- [Process Gap Analysis](process_gap_analysis.md)
6. Queue Package
- [Queue Package Specification](queue_package_specification.md)
- [Queue Gap Analysis](queue_gap_analysis.md)
7. Route Package
- [Route Package Specification](route_package_specification.md)
- [Route Gap Analysis](route_gap_analysis.md)
8. Testing Package
- [Testing Package Specification](testing_package_specification.md)
- [Testing Gap Analysis](testing_gap_analysis.md)
## Core Integration Points
### 1. Container Integration Hub
```dart
// All packages integrate with Container for dependency injection
class ServiceProvider {
final Container container;
// Current Integration
void register() {
container.registerSingleton<Service>(ServiceImpl());
}
// Laravel-Compatible Enhancement
void register() {
// Add contextual binding
container.when(Service).needs<Logger>().give(FileLogger());
// Add tagged binding
container.tag([
EmailNotifier,
SmsNotifier,
PushNotifier
], 'notifications');
}
}
```
### 2. Event System Integration
```dart
// Events package integrates with multiple packages
class EventServiceProvider {
// Current Integration
void register() {
// Queue Integration
container.singleton<QueuedEventDispatcher>((c) =>
QueuedEventDispatcher(
queue: c.make<QueueContract>(),
broadcaster: c.make<BroadcasterContract>()
)
);
// Bus Integration
container.singleton<CommandDispatcher>((c) =>
CommandDispatcher(
events: c.make<EventDispatcherContract>(),
queue: c.make<QueueContract>()
)
);
}
// Laravel-Compatible Enhancement
void register() {
// Add event discovery
container.singleton<EventDiscovery>((c) =>
EventDiscovery(c.make<Reflector>())
);
// Add after commit handling
container.singleton<DatabaseEventDispatcher>((c) =>
DatabaseEventDispatcher(
events: c.make<EventDispatcherContract>(),
db: c.make<DatabaseManager>()
)
);
}
}
```
### 3. Queue and Bus Integration
```dart
// Queue and Bus packages work together for job handling
class QueuedCommandDispatcher {
// Current Integration
Future<void> dispatch(Command command) async {
if (command is ShouldQueue) {
await queue.push(QueuedCommandJob(command));
} else {
await commandBus.dispatch(command);
}
}
// Laravel-Compatible Enhancement
Future<void> dispatch(Command command) async {
// Add job middleware
var job = QueuedCommandJob(command)
..through([
RateLimitedMiddleware(),
WithoutOverlappingMiddleware()
]);
// Add job batching
if (command is BatchableCommand) {
await queue.batch([job])
.allowFailures()
.dispatch();
} else {
await queue.push(job);
}
}
}
```
### 4. Route and Core Integration
```dart
// Route package integrates with Core for HTTP handling
class RouterServiceProvider {
// Current Integration
void register() {
container.singleton<Router>((c) =>
Router()
..use(LoggingMiddleware())
..use(AuthMiddleware())
);
}
// Laravel-Compatible Enhancement
void register() {
// Add model binding
container.singleton<RouteModelBinder>((c) =>
RouteModelBinder(
models: c.make<ModelRegistry>(),
db: c.make<DatabaseManager>()
)
);
// Add subdomain routing
container.singleton<SubdomainRouter>((c) =>
SubdomainRouter(
domains: c.make<DomainRegistry>(),
router: c.make<Router>()
)
);
}
}
```
### 5. Process and Queue Integration
```dart
// Process package integrates with Queue for background tasks
class ProcessManager {
// Current Integration
Future<void> runProcess(String command) async {
var process = await Process.start(command);
queue.push(ProcessMonitorJob(process.pid));
}
// Laravel-Compatible Enhancement
Future<void> runProcess(String command) async {
// Add scheduling
scheduler.job(ProcessJob(command))
.everyFiveMinutes()
.withoutOverlapping()
.onFailure((e) => notifyAdmin(e));
// Add process pools
processPool.job(command)
.onServers(['worker-1', 'worker-2'])
.dispatch();
}
}
```
### 6. Model and Event Integration
```dart
// Model package integrates with Events for model events
class ModelEventDispatcher {
// Current Integration
Future<void> save(Model model) async {
await events.dispatch(ModelSaving(model));
await db.save(model);
await events.dispatch(ModelSaved(model));
}
// Laravel-Compatible Enhancement
Future<void> save(Model model) async {
// Add transaction awareness
await db.transaction((tx) async {
await events.dispatch(ModelSaving(model));
await tx.save(model);
// Queue after commit
events.afterCommit(() =>
events.dispatch(ModelSaved(model))
);
});
}
}
```
## Package-Specific Integration Points
### 1. Container Package
```dart
// Integration with other packages
class Container {
// Current
void bootstrap() {
registerSingleton<EventDispatcher>();
registerSingleton<QueueManager>();
registerSingleton<CommandBus>();
}
// Enhanced
void bootstrap() {
// Add service repository
registerSingleton<ServiceRepository>((c) =>
ServiceRepository([
EventServiceProvider(),
QueueServiceProvider(),
BusServiceProvider()
])
);
// Add deferred loading
registerDeferred<ReportGenerator>((c) =>
ReportGenerator(c.make<Database>())
);
}
}
```
### 2. Events Package
```dart
// Integration with other packages
class EventDispatcher {
// Current
Future<void> dispatch(Event event) async {
if (event is QueuedEvent) {
await queue.push(event);
} else {
await notifyListeners(event);
}
}
// Enhanced
Future<void> dispatch(Event event) async {
// Add broadcast channels
if (event is BroadcastEvent) {
await broadcast.to(event.channels)
.with(['queue' => queue.connection()])
.send(event);
}
// Add event subscribers
await container.make<EventSubscriberRegistry>()
.dispatch(event);
}
}
```
### 3. Queue Package
```dart
// Integration with other packages
class QueueManager {
// Current
Future<void> process() async {
while (true) {
var job = await queue.pop();
await job.handle();
}
}
// Enhanced
Future<void> process() async {
// Add worker management
worker.supervise((worker) {
worker.process('default')
.throughMiddleware([
RateLimited::class,
PreventOverlapping::class
])
.withEvents(events);
});
}
}
```
## Integration Enhancement Strategy
1. **Container Enhancements**
- Add contextual binding
- Add tagged bindings
- Keep existing integrations working
2. **Event System Enhancements**
- Add event discovery
- Add after commit handling
- Maintain existing event flow
3. **Queue System Enhancements**
- Add job batching
- Add better job middleware
- Keep existing job handling
4. **Route System Enhancements**
- Add model binding
- Add subdomain routing
- Maintain existing routing
5. **Process System Enhancements**
- Add scheduling
- Add process pools
- Keep existing process management
6. **Model System Enhancements**
- Add Eloquent features
- Add relationships
- Maintain existing model events
## Implementation Steps
1. **Document Current Integration Points**
- Map all package dependencies
- Document integration interfaces
- Note existing functionality
2. **Plan Laravel-Compatible Interfaces**
- Review Laravel's interfaces
- Design compatible interfaces
- Plan migration strategy
3. **Implement Enhancements**
- Start with Container enhancements
- Add Event enhancements
- Add Queue enhancements
- Continue with other packages
4. **Test Integration Points**
- Test existing functionality
- Test new functionality
- Test Laravel compatibility
5. **Migration Guide**
- Document breaking changes
- Provide upgrade path
- Include examples
## Package Dependencies
```mermaid
graph TD
Core[Core] --> Container[Container]
Core --> Events[Events]
Core --> Pipeline[Pipeline]
Container --> Contracts[Contracts]
Events --> Container
Pipeline --> Container
Bus[Bus] --> Events
Bus --> Queue[Queue]
Config[Config] --> Container
Filesystem[Filesystem] --> Container
Model[Model] --> Events
Model --> Container
Process[Process] --> Events
Process --> Queue
Queue --> Events
Queue --> Container
Route[Route] --> Pipeline
Route --> Container
Testing[Testing] --> Container
Testing --> Events
```
## Implementation Status
### Core Framework (90%)
- Core Package (95%)
* Application lifecycle ✓
* Service providers ✓
* HTTP kernel ✓
* Console kernel ✓
* Exception handling ✓
* Needs: Performance optimizations
- Container Package (90%)
* Basic DI ✓
* Auto-wiring ✓
* Service providers ✓
* Needs: Contextual binding
- Events Package (85%)
* Event dispatching ✓
* Event subscribers ✓
* Event broadcasting ✓
* Needs: Event discovery
### Infrastructure (80%)
- Bus Package (85%)
* Command dispatching ✓
* Command queuing ✓
* Needs: Command batching
- Config Package (80%)
* Configuration repository ✓
* Environment loading ✓
* Needs: Config caching
- Filesystem Package (75%)
* Local driver ✓
* Cloud storage ✓
* Needs: Streaming support
- Model Package (80%)
* Basic ORM ✓
* Relationships ✓
* Needs: Model events
- Process Package (85%)
* Process management ✓
* Process pools ✓
* Needs: Process monitoring
- Queue Package (85%)
* Queue workers ✓
* Job batching ✓
* Needs: Rate limiting
- Route Package (90%)
* Route registration ✓
* Route matching ✓
* Middleware ✓
* Needs: Route caching
- Testing Package (85%)
* HTTP testing ✓
* Database testing ✓
* Needs: Browser testing
## Development Guidelines
### 1. Getting Started
Before implementing integrations:
1. Review [Getting Started Guide](getting_started.md)
2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Follow [Testing Guide](testing_guide.md)
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
### 2. Implementation Process
For each integration:
1. Write tests following [Testing Guide](testing_guide.md)
2. Implement following Laravel patterns
3. Document following [Getting Started Guide](getting_started.md#documentation)
4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md)
### 3. Quality Requirements
All integrations must:
1. Pass all tests (see [Testing Guide](testing_guide.md))
2. Meet Laravel compatibility requirements
3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md))
4. Match package specifications
### 4. Documentation Requirements
Integration documentation must:
1. Explain integration patterns
2. Show usage examples
3. Cover error handling
4. Include performance tips
5. Follow standards in [Getting Started Guide](getting_started.md#documentation)

View file

@ -0,0 +1,316 @@
# Pipeline Package Gap Analysis
## Overview
This document analyzes the gaps between our Pipeline package's actual implementation and our documentation, identifying areas that need implementation or documentation updates.
> **Related Documentation**
> - See [Pipeline Package Specification](pipeline_package_specification.md) for current implementation
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for overall status
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Testing Guide](testing_guide.md) for testing approaches
> - See [Getting Started Guide](getting_started.md) for development setup
## Implementation Gaps
### 1. Missing Laravel Features
```dart
// Documented but not implemented:
// 1. Pipeline Hub
class PipelineHub {
// Need to implement:
Pipeline pipeline(String name);
void defaults(List<Pipe> pipes);
Pipeline middleware();
Pipeline bus();
}
// 2. Pipeline Conditions
class Pipeline {
// Need to implement:
Pipeline when(bool Function() callback);
Pipeline unless(bool Function() callback);
Pipeline whenCallback(Function callback);
}
// 3. Pipeline Caching
class Pipeline {
// Need to implement:
void enableCache();
void clearCache();
dynamic getCached(String key);
}
```
### 2. Existing Features Not Documented
```dart
// Implemented but not documented:
// 1. Type Registration
class Pipeline {
/// Registers pipe types for string resolution
void registerPipeType(String name, Type type);
/// Type map for string resolution
final Map<String, Type> _typeMap = {};
}
// 2. Method Invocation
class Pipeline {
/// Invokes methods on pipe instances
Future<dynamic> invokeMethod(
dynamic instance,
String methodName,
List<dynamic> arguments
);
/// Sets method to call on pipes
Pipeline via(String method);
}
// 3. Exception Handling
class Pipeline {
/// Logger for pipeline
final Logger _logger;
/// Handles exceptions in pipeline
dynamic handleException(dynamic passable, Object e);
}
```
### 3. Integration Points Not Documented
```dart
// 1. Container Integration
class Pipeline {
/// Container reference
final Container? _container;
/// Gets container instance
Container getContainer();
/// Sets container instance
Pipeline setContainer(Container container);
}
// 2. Reflection Integration
class Pipeline {
/// Resolves pipe types using mirrors
Type? _resolvePipeType(String pipeClass) {
try {
for (var lib in currentMirrorSystem().libraries.values) {
// Reflection logic...
}
} catch (_) {}
}
}
// 3. Logging Integration
class Pipeline {
/// Logger instance
final Logger _logger;
/// Logs pipeline events
void _logPipelineEvent(String message, [Object? error]);
}
```
## Documentation Gaps
### 1. Missing API Documentation
```dart
// Need to document:
/// Registers a pipe type for string resolution.
///
/// This allows pipes to be specified by string names in the through() method.
///
/// Example:
/// ```dart
/// pipeline.registerPipeType('auth', AuthMiddleware);
/// pipeline.through(['auth']);
/// ```
void registerPipeType(String name, Type type);
/// Sets the method to be called on pipe instances.
///
/// By default, the 'handle' method is called. This method allows
/// customizing which method is called on each pipe.
///
/// Example:
/// ```dart
/// pipeline.via('process').through([MyPipe]);
/// // Will call process() instead of handle()
/// ```
Pipeline via(String method);
```
### 2. Missing Integration Examples
```dart
// Need examples for:
// 1. Container Integration
var pipeline = Pipeline(container)
..through([
AuthMiddleware,
container.make<LoggingMiddleware>(),
'validation' // Resolved from container
]);
// 2. Exception Handling
pipeline.through([
(passable, next) async {
try {
return await next(passable);
} catch (e) {
logger.error('Pipeline error', e);
throw PipelineException(e.toString());
}
}
]);
// 3. Method Customization
pipeline.via('process')
.through([
ProcessingPipe(), // Will call process() instead of handle()
ValidationPipe()
]);
```
### 3. Missing Test Coverage
```dart
// Need tests for:
void main() {
group('Type Registration', () {
test('resolves string pipes to types', () {
var pipeline = Pipeline(container);
pipeline.registerPipeType('auth', AuthMiddleware);
await pipeline
.through(['auth'])
.send(request)
.then(handler);
verify(() => container.make<AuthMiddleware>()).called(1);
});
});
group('Method Invocation', () {
test('calls custom methods on pipes', () {
var pipeline = Pipeline(container);
var pipe = MockPipe();
await pipeline
.via('process')
.through([pipe])
.send(data)
.then(handler);
verify(() => pipe.process(any, any)).called(1);
});
});
}
```
## Implementation Priority
1. **High Priority**
- Pipeline hub (Laravel compatibility)
- Pipeline conditions (Laravel compatibility)
- Better exception handling
2. **Medium Priority**
- Pipeline caching
- Better type resolution
- Performance optimizations
3. **Low Priority**
- Additional helper methods
- Extended testing utilities
- Debug/profiling tools
## Next Steps
1. **Implementation Tasks**
- Add pipeline hub
- Add pipeline conditions
- Add caching support
- Improve exception handling
2. **Documentation Tasks**
- Document type registration
- Document method invocation
- Document exception handling
- Add integration examples
3. **Testing Tasks**
- Add type registration tests
- Add method invocation tests
- Add exception handling tests
- Add integration tests
Would you like me to:
1. Start implementing missing features?
2. Update documentation for existing features?
3. Create test cases for missing coverage?
## Development Guidelines
### 1. Getting Started
Before implementing pipeline features:
1. Review [Getting Started Guide](getting_started.md)
2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Follow [Testing Guide](testing_guide.md)
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
5. Review [Pipeline Package Specification](pipeline_package_specification.md)
### 2. Implementation Process
For each pipeline feature:
1. Write tests following [Testing Guide](testing_guide.md)
2. Implement following Laravel patterns
3. Document following [Getting Started Guide](getting_started.md#documentation)
4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md)
### 3. Quality Requirements
All implementations must:
1. Pass all tests (see [Testing Guide](testing_guide.md))
2. Meet Laravel compatibility requirements
3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md))
4. Match specifications in [Pipeline Package Specification](pipeline_package_specification.md)
### 4. Integration Considerations
When implementing pipeline features:
1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md)
2. Ensure Laravel compatibility per [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Use testing approaches from [Testing Guide](testing_guide.md)
4. Follow development setup in [Getting Started Guide](getting_started.md)
### 5. Performance Guidelines
Pipeline system must:
1. Handle nested pipelines efficiently
2. Minimize memory usage in long pipelines
3. Support async operations
4. Scale with number of stages
5. Meet performance targets in [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#performance-benchmarks)
### 6. Testing Requirements
Pipeline tests must:
1. Cover all pipeline types
2. Test stage ordering
3. Verify error handling
4. Check conditional execution
5. Follow patterns in [Testing Guide](testing_guide.md)
### 7. Documentation Requirements
Pipeline documentation must:
1. Explain pipeline patterns
2. Show integration examples
3. Cover error handling
4. Include performance tips
5. Follow standards in [Getting Started Guide](getting_started.md#documentation)

View file

@ -0,0 +1,408 @@
# Pipeline Package Specification
## Overview
The Pipeline package provides a robust implementation of the pipeline pattern, allowing for the sequential processing of tasks through a series of stages. It integrates deeply with our Route, Bus, and Queue packages while maintaining Laravel compatibility.
> **Related Documentation**
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for implementation status
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Testing Guide](testing_guide.md) for testing approaches
> - See [Getting Started Guide](getting_started.md) for development setup
> - See [Contracts Package Specification](contracts_package_specification.md) for pipeline contracts
## Core Features
### 1. Pipeline Base
```dart
/// Core pipeline class with conditional execution
class Pipeline<TPassable> with Conditionable<Pipeline<TPassable>> {
final Container _container;
final List<Pipe<TPassable>> _pipes;
TPassable? _passable;
String _method = 'handle';
Pipeline(this._container, [List<Pipe<TPassable>>? pipes])
: _pipes = pipes ?? [];
/// Sends an object through the pipeline
Pipeline<TPassable> send(TPassable passable) {
_passable = passable;
return this;
}
/// Sets the stages of the pipeline
Pipeline<TPassable> through(List<dynamic> pipes) {
for (var pipe in pipes) {
if (pipe is String) {
// Resolve from container
_pipes.add(_container.make(pipe));
} else if (pipe is Type) {
_pipes.add(_container.make(pipe));
} else if (pipe is Pipe<TPassable>) {
_pipes.add(pipe);
} else if (pipe is Function) {
_pipes.add(FunctionPipe(pipe));
}
}
return this;
}
/// Sets the method to call on the pipes
Pipeline<TPassable> via(String method) {
_method = method;
return this;
}
/// Process the pipeline to final result
Future<TResult> then<TResult>(
FutureOr<TResult> Function(TPassable) destination
) async {
var pass = _passable;
if (pass == null) {
throw PipelineException('No passable object provided');
}
// Build pipeline
var pipeline = _pipes.fold<Function>(
destination,
(next, pipe) => (passable) =>
_container.call(() => pipe.handle(passable, next))
);
// Execute pipeline
return await pipeline(pass);
}
}
```
### 2. Middleware Pipeline
```dart
/// HTTP middleware pipeline with route integration
class MiddlewarePipeline extends Pipeline<Request> {
final Router _router;
MiddlewarePipeline(Container container, this._router)
: super(container);
/// Adds route-specific middleware
MiddlewarePipeline throughRoute(Route route) {
// Get global middleware
var middleware = _router.middleware;
// Add route middleware
if (route.middleware.isNotEmpty) {
middleware.addAll(
route.middleware.map((m) => _container.make<Middleware>(m))
);
}
// Add route group middleware
if (route.group != null) {
middleware.addAll(route.group!.middleware);
}
return through(middleware);
}
/// Processes request through middleware
Future<Response> process(
Request request,
FutureOr<Response> Function(Request) destination
) {
return send(request)
.when(() => shouldProcessMiddleware(request))
.then(destination);
}
/// Checks if middleware should be processed
bool shouldProcessMiddleware(Request request) {
return !request.attributes.containsKey('skip_middleware');
}
}
```
### 3. Bus Pipeline
```dart
/// Command bus pipeline with handler resolution
class BusPipeline<TCommand> extends Pipeline<TCommand> {
final CommandBus _bus;
BusPipeline(Container container, this._bus)
: super(container);
/// Processes command through pipeline
Future<TResult> process<TResult>(
TCommand command,
[Handler<TCommand>? handler]
) {
// Resolve handler
handler ??= _resolveHandler(command);
return send(command).then((cmd) =>
handler!.handle(cmd) as Future<TResult>
);
}
/// Resolves command handler
Handler<TCommand> _resolveHandler(TCommand command) {
if (command is Command) {
return _container.make(command.handler);
}
var handlerType = _bus.handlers[TCommand];
if (handlerType == null) {
throw HandlerNotFoundException(
'No handler found for ${TCommand}'
);
}
return _container.make(handlerType);
}
}
```
### 4. Job Pipeline
```dart
/// Queue job pipeline with middleware
class JobPipeline extends Pipeline<Job> {
final QueueManager _queue;
JobPipeline(Container container, this._queue)
: super(container);
/// Processes job through pipeline
Future<void> process(Job job) {
return send(job)
.through(_queue.middleware)
.then((j) => j.handle());
}
/// Adds rate limiting
JobPipeline withRateLimit(int maxAttempts, Duration timeout) {
return through([
RateLimitedPipe(maxAttempts, timeout)
]);
}
/// Prevents overlapping jobs
JobPipeline withoutOverlapping() {
return through([WithoutOverlappingPipe()]);
}
}
```
### 5. Pipeline Hub
```dart
/// Manages application pipelines
class PipelineHub {
final Container _container;
final Map<String, Pipeline> _pipelines = {};
final List<Pipe> _defaults = [];
PipelineHub(this._container);
/// Gets or creates a pipeline
Pipeline pipeline(String name) {
return _pipelines.putIfAbsent(
name,
() => Pipeline(_container, [..._defaults])
);
}
/// Gets middleware pipeline
MiddlewarePipeline middleware() {
return pipeline('middleware') as MiddlewarePipeline;
}
/// Gets bus pipeline
BusPipeline bus() {
return pipeline('bus') as BusPipeline;
}
/// Gets job pipeline
JobPipeline job() {
return pipeline('job') as JobPipeline;
}
/// Sets default pipes
void defaults(List<Pipe> pipes) {
_defaults.addAll(pipes);
}
}
```
## Integration Examples
### 1. Route Integration
```dart
// In RouteServiceProvider
void boot() {
router.middleware([
StartSession::class,
VerifyCsrfToken::class
]);
router.group(['middleware' => ['auth']], () {
router.get('/dashboard', DashboardController);
});
}
// In Router
Future<Response> dispatch(Request request) {
var route = matchRoute(request);
return container.make<MiddlewarePipeline>()
.throughRoute(route)
.process(request, (req) => route.handle(req));
}
```
### 2. Command Bus Integration
```dart
// In CommandBus
Future<TResult> dispatch<TResult>(Command command) {
return container.make<BusPipeline>()
.through([
TransactionPipe(),
ValidationPipe(),
AuthorizationPipe()
])
.process<TResult>(command);
}
// Usage
class CreateOrder implements Command {
@override
Type get handler => CreateOrderHandler;
}
var order = await bus.dispatch<Order>(
CreateOrder(items: items)
);
```
### 3. Queue Integration
```dart
// In QueueWorker
Future<void> process(Job job) {
return container.make<JobPipeline>()
.withRateLimit(3, Duration(minutes: 1))
.withoutOverlapping()
.process(job);
}
// Usage
class ProcessPayment implements Job {
@override
Future<void> handle() async {
// Process payment
}
}
await queue.push(ProcessPayment(
orderId: order.id
));
```
## Testing
```dart
void main() {
group('Middleware Pipeline', () {
test('processes route middleware', () async {
var pipeline = MiddlewarePipeline(container, router);
var route = Route('/test', middleware: ['auth']);
var response = await pipeline
.throughRoute(route)
.process(request, handler);
verify(() => auth.handle(any, any)).called(1);
});
});
group('Bus Pipeline', () {
test('resolves and executes handler', () async {
var pipeline = BusPipeline(container, bus);
var command = CreateOrder(items: items);
var result = await pipeline.process<Order>(command);
expect(result, isA<Order>());
verify(() => handler.handle(command)).called(1);
});
});
}
```
## Next Steps
1. Add more middleware types
2. Enhance bus pipeline features
3. Add job pipeline features
4. Improve testing coverage
5. Add performance optimizations
Would you like me to enhance any other package specifications?
## Development Guidelines
### 1. Getting Started
Before implementing pipeline features:
1. Review [Getting Started Guide](getting_started.md)
2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Follow [Testing Guide](testing_guide.md)
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
5. Understand [Contracts Package Specification](contracts_package_specification.md)
### 2. Implementation Process
For each pipeline feature:
1. Write tests following [Testing Guide](testing_guide.md)
2. Implement following Laravel patterns
3. Document following [Getting Started Guide](getting_started.md#documentation)
4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md)
### 3. Quality Requirements
All implementations must:
1. Pass all tests (see [Testing Guide](testing_guide.md))
2. Meet Laravel compatibility requirements
3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md))
4. Implement required contracts (see [Contracts Package Specification](contracts_package_specification.md))
### 4. Integration Considerations
When implementing pipelines:
1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md)
2. Ensure Laravel compatibility per [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Use testing approaches from [Testing Guide](testing_guide.md)
4. Follow development setup in [Getting Started Guide](getting_started.md)
5. Implement all contracts from [Contracts Package Specification](contracts_package_specification.md)
### 5. Performance Guidelines
Pipeline system must:
1. Handle nested pipelines efficiently
2. Minimize memory usage in long pipelines
3. Support async operations
4. Scale with number of stages
5. Meet performance targets in [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#performance-benchmarks)
### 6. Testing Requirements
Pipeline tests must:
1. Cover all pipeline types
2. Test stage ordering
3. Verify error handling
4. Check conditional execution
5. Follow patterns in [Testing Guide](testing_guide.md)
### 7. Documentation Requirements
Pipeline documentation must:
1. Explain pipeline patterns
2. Show integration examples
3. Cover error handling
4. Include performance tips
5. Follow standards in [Getting Started Guide](getting_started.md#documentation)

View file

@ -0,0 +1,299 @@
# Process Package Gap Analysis
## Overview
This document analyzes the gaps between our Process package's actual implementation and Laravel's process functionality, identifying areas that need implementation or documentation updates.
> **Related Documentation**
> - See [Process Package Specification](process_package_specification.md) for current implementation
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for overall status
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Testing Guide](testing_guide.md) for testing approaches
> - See [Getting Started Guide](getting_started.md) for development setup
> - See [Events Package Specification](events_package_specification.md) for process events
> - See [Queue Package Specification](queue_package_specification.md) for background processing
## Implementation Gaps
### 1. Missing Laravel Features
```dart
// Documented but not implemented:
// 1. Process Pipelines
class ProcessPipeline {
// Need to implement:
Future<ProcessResult> pipe(String command);
Future<ProcessResult> pipeThrough(List<String> commands);
Future<ProcessResult> pipeInput(String input);
Future<ProcessResult> pipeOutput(String file);
Future<ProcessResult> pipeErrorOutput(String file);
}
// 2. Process Scheduling
class ProcessScheduler {
// Need to implement:
void schedule(String command, String frequency);
void daily(String command, [String time = '00:00']);
void weekly(String command, [int day = 0]);
void monthly(String command, [int day = 1]);
void cron(String expression, String command);
}
// 3. Process Monitoring
class ProcessMonitor {
// Need to implement:
Future<bool> isRunning(int pid);
Future<ProcessStats> getStats(int pid);
Future<void> onExit(int pid, Function callback);
Future<void> onOutput(int pid, Function callback);
Future<void> onError(int pid, Function callback);
}
```
### 2. Missing Process Features
```dart
// Need to implement:
// 1. Process Groups
class ProcessGroup {
// Need to implement:
Future<void> start();
Future<void> stop();
Future<void> restart();
Future<List<ProcessResult>> wait();
Future<void> signal(ProcessSignal signal);
bool isRunning();
}
// 2. Process Isolation
class ProcessIsolation {
// Need to implement:
void setUser(String user);
void setGroup(String group);
void setWorkingDirectory(String directory);
void setEnvironment(Map<String, String> env);
void setResourceLimits(ResourceLimits limits);
}
// 3. Process Recovery
class ProcessRecovery {
// Need to implement:
void onCrash(Function callback);
void onHang(Function callback);
void onHighMemory(Function callback);
void onHighCpu(Function callback);
void restart();
}
```
### 3. Missing Integration Features
```dart
// Need to implement:
// 1. Queue Integration
class QueuedProcess {
// Need to implement:
Future<void> queue(String command);
Future<void> laterOn(String queue, Duration delay, String command);
Future<void> chain(List<String> commands);
Future<void> release(Duration delay);
}
// 2. Event Integration
class ProcessEvents {
// Need to implement:
void beforeStart(Function callback);
void afterStart(Function callback);
void beforeStop(Function callback);
void afterStop(Function callback);
void onOutput(Function callback);
void onError(Function callback);
}
// 3. Logging Integration
class ProcessLogging {
// Need to implement:
void enableLogging();
void setLogFile(String path);
void setLogLevel(LogLevel level);
void rotateLog();
void purgeOldLogs(Duration age);
}
```
## Documentation Gaps
### 1. Missing API Documentation
```dart
// Need to document:
/// Pipes process output.
///
/// Example:
/// ```dart
/// await process
/// .pipe('sort')
/// .pipe('uniq')
/// .pipeOutput('output.txt');
/// ```
Future<ProcessResult> pipe(String command);
/// Schedules process execution.
///
/// Example:
/// ```dart
/// scheduler.daily('backup.sh', '02:00');
/// scheduler.weekly('cleanup.sh', DateTime.sunday);
/// scheduler.cron('0 * * * *', 'hourly.sh');
/// ```
void schedule(String command, String frequency);
```
### 2. Missing Integration Examples
```dart
// Need examples for:
// 1. Process Groups
var group = ProcessGroup();
group.add('web-server', '--port=8080');
group.add('worker', '--queue=default');
await group.start();
// 2. Process Recovery
var recovery = ProcessRecovery(process);
recovery.onCrash(() async {
await notifyAdmin('Process crashed');
await process.restart();
});
// 3. Process Monitoring
var monitor = ProcessMonitor(process);
monitor.onHighMemory((usage) async {
await process.restart();
await notifyAdmin('High memory usage: $usage');
});
```
### 3. Missing Test Coverage
```dart
// Need tests for:
void main() {
group('Process Pipelines', () {
test('pipes process output', () async {
var process = await manager.start('ls');
var result = await process
.pipe('sort')
.pipe('uniq')
.pipeOutput('output.txt');
expect(result.exitCode, equals(0));
expect(File('output.txt').existsSync(), isTrue);
});
});
group('Process Scheduling', () {
test('schedules daily tasks', () async {
var scheduler = ProcessScheduler();
scheduler.daily('backup.sh', '02:00');
var nextRun = scheduler.getNextRun('backup.sh');
expect(nextRun.hour, equals(2));
});
});
}
```
## Implementation Priority
1. **High Priority**
- Process pipelines (Laravel compatibility)
- Process scheduling (Laravel compatibility)
- Process monitoring
2. **Medium Priority**
- Process groups
- Process isolation
- Process recovery
3. **Low Priority**
- Additional integration features
- Additional monitoring features
- Performance optimizations
## Next Steps
1. **Implementation Tasks**
- Add process pipelines
- Add process scheduling
- Add process monitoring
- Add process groups
2. **Documentation Tasks**
- Document pipelines
- Document scheduling
- Document monitoring
- Add integration examples
3. **Testing Tasks**
- Add pipeline tests
- Add scheduling tests
- Add monitoring tests
- Add group tests
## Development Guidelines
### 1. Getting Started
Before implementing process features:
1. Review [Getting Started Guide](getting_started.md)
2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Follow [Testing Guide](testing_guide.md)
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
5. Review [Process Package Specification](process_package_specification.md)
6. Review [Events Package Specification](events_package_specification.md)
7. Review [Queue Package Specification](queue_package_specification.md)
### 2. Implementation Process
For each process feature:
1. Write tests following [Testing Guide](testing_guide.md)
2. Implement following Laravel patterns
3. Document following [Getting Started Guide](getting_started.md#documentation)
4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md)
### 3. Quality Requirements
All implementations must:
1. Pass all tests (see [Testing Guide](testing_guide.md))
2. Meet Laravel compatibility requirements
3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md))
4. Match specifications in [Process Package Specification](process_package_specification.md)
### 4. Integration Considerations
When implementing process features:
1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md)
2. Ensure Laravel compatibility per [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Use testing approaches from [Testing Guide](testing_guide.md)
4. Follow development setup in [Getting Started Guide](getting_started.md)
### 5. Performance Guidelines
Process system must:
1. Handle concurrent processes efficiently
2. Manage system resources
3. Support process pooling
4. Scale with process count
5. Meet performance targets in [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#performance-benchmarks)
### 6. Testing Requirements
Process tests must:
1. Cover all process operations
2. Test concurrent execution
3. Verify event handling
4. Check resource cleanup
5. Follow patterns in [Testing Guide](testing_guide.md)
### 7. Documentation Requirements
Process documentation must:
1. Explain process patterns
2. Show pipeline examples
3. Cover error handling
4. Include performance tips
5. Follow standards in [Getting Started Guide](getting_started.md#documentation)

View file

@ -0,0 +1,408 @@
# Process Package Specification
## Overview
The Process package provides a robust system process handling system that matches Laravel's process functionality. It supports process execution, input/output handling, process pools, and signal handling while integrating with our Event and Queue packages.
> **Related Documentation**
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for implementation status
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Testing Guide](testing_guide.md) for testing approaches
> - See [Getting Started Guide](getting_started.md) for development setup
> - See [Events Package Specification](events_package_specification.md) for process events
> - See [Queue Package Specification](queue_package_specification.md) for background processing
## Core Features
### 1. Process Manager
```dart
/// Core process manager implementation
class ProcessManager implements ProcessContract {
/// Container instance
final Container _container;
/// Active processes
final Map<int, Process> _processes = {};
/// Process event dispatcher
final EventDispatcherContract _events;
ProcessManager(this._container, this._events);
/// Starts a process
Future<Process> start(
String command, [
List<String>? arguments,
ProcessOptions? options
]) async {
options ??= ProcessOptions();
var process = await Process.start(
command,
arguments ?? [],
workingDirectory: options.workingDirectory,
environment: options.environment,
includeParentEnvironment: options.includeParentEnvironment,
runInShell: options.runInShell
);
_processes[process.pid] = process;
await _events.dispatch(ProcessStarted(process));
return process;
}
/// Runs a process to completion
Future<ProcessResult> run(
String command, [
List<String>? arguments,
ProcessOptions? options
]) async {
var process = await start(command, arguments, options);
var result = await process.exitCode;
await _events.dispatch(ProcessCompleted(
process,
result
));
return ProcessResult(
process.pid,
result,
await _readOutput(process.stdout),
await _readOutput(process.stderr)
);
}
/// Kills a process
Future<void> kill(int pid, [ProcessSignal signal = ProcessSignal.sigterm]) async {
var process = _processes[pid];
if (process == null) return;
process.kill(signal);
await _events.dispatch(ProcessKilled(process));
_processes.remove(pid);
}
/// Gets active processes
List<Process> get activeProcesses => List.from(_processes.values);
/// Reads process output
Future<String> _readOutput(Stream<List<int>> stream) async {
var buffer = StringBuffer();
await for (var data in stream) {
buffer.write(String.fromCharCodes(data));
}
return buffer.toString();
}
}
```
### 2. Process Pool
```dart
/// Process pool for parallel execution
class ProcessPool {
/// Maximum concurrent processes
final int concurrency;
/// Process manager
final ProcessManager _manager;
/// Active processes
final Set<Process> _active = {};
/// Pending commands
final Queue<PendingCommand> _pending = Queue();
ProcessPool(this._manager, {this.concurrency = 5});
/// Starts a process in the pool
Future<ProcessResult> start(
String command, [
List<String>? arguments,
ProcessOptions? options
]) async {
var pending = PendingCommand(
command,
arguments,
options
);
_pending.add(pending);
await _processQueue();
return await pending.future;
}
/// Processes pending commands
Future<void> _processQueue() async {
while (_active.length < concurrency && _pending.isNotEmpty) {
var command = _pending.removeFirst();
await _startProcess(command);
}
}
/// Starts a process
Future<void> _startProcess(PendingCommand command) async {
var process = await _manager.start(
command.command,
command.arguments,
command.options
);
_active.add(process);
process.exitCode.then((result) {
_active.remove(process);
command.complete(ProcessResult(
process.pid,
result,
'',
''
));
_processQueue();
});
}
}
```
### 3. Process Events
```dart
/// Process started event
class ProcessStarted {
/// The started process
final Process process;
ProcessStarted(this.process);
}
/// Process completed event
class ProcessCompleted {
/// The completed process
final Process process;
/// Exit code
final int exitCode;
ProcessCompleted(this.process, this.exitCode);
}
/// Process killed event
class ProcessKilled {
/// The killed process
final Process process;
ProcessKilled(this.process);
}
/// Process failed event
class ProcessFailed {
/// The failed process
final Process process;
/// Error details
final Object error;
ProcessFailed(this.process, this.error);
}
```
### 4. Process Options
```dart
/// Process execution options
class ProcessOptions {
/// Working directory
final String? workingDirectory;
/// Environment variables
final Map<String, String>? environment;
/// Include parent environment
final bool includeParentEnvironment;
/// Run in shell
final bool runInShell;
/// Process timeout
final Duration? timeout;
/// Idle timeout
final Duration? idleTimeout;
/// Retry attempts
final int retryAttempts;
/// Retry delay
final Duration retryDelay;
ProcessOptions({
this.workingDirectory,
this.environment,
this.includeParentEnvironment = true,
this.runInShell = false,
this.timeout,
this.idleTimeout,
this.retryAttempts = 0,
this.retryDelay = const Duration(seconds: 1)
});
}
```
## Integration Examples
### 1. Basic Process Execution
```dart
// Run process
var result = await processManager.run('ls', ['-la']);
print('Output: ${result.stdout}');
print('Exit code: ${result.exitCode}');
// Start long-running process
var process = await processManager.start('server', ['--port=8080']);
await process.exitCode; // Wait for completion
```
### 2. Process Pool
```dart
// Create process pool
var pool = ProcessPool(processManager, concurrency: 3);
// Run multiple processes
await Future.wait([
pool.start('task1'),
pool.start('task2'),
pool.start('task3'),
pool.start('task4') // Queued until slot available
]);
```
### 3. Process Events
```dart
// Listen for process events
events.listen<ProcessStarted>((event) {
print('Process ${event.process.pid} started');
});
events.listen<ProcessCompleted>((event) {
print('Process ${event.process.pid} completed with code ${event.exitCode}');
});
// Start process
await processManager.start('long-task');
```
## Testing
```dart
void main() {
group('Process Manager', () {
test('runs processes', () async {
var manager = ProcessManager(container, events);
var result = await manager.run('echo', ['Hello']);
expect(result.exitCode, equals(0));
expect(result.stdout, contains('Hello'));
});
test('handles process failure', () async {
var manager = ProcessManager(container, events);
expect(
() => manager.run('invalid-command'),
throwsA(isA<ProcessException>())
);
});
});
group('Process Pool', () {
test('limits concurrent processes', () async {
var pool = ProcessPool(manager, concurrency: 2);
var started = <String>[];
events.listen<ProcessStarted>((event) {
started.add(event.process.pid.toString());
});
await Future.wait([
pool.start('task1'),
pool.start('task2'),
pool.start('task3')
]);
expect(started.length, equals(3));
expect(started.take(2).length, equals(2));
});
});
}
```
## Next Steps
1. Implement core process features
2. Add process pool
3. Add process events
4. Add retry handling
5. Write tests
6. Add benchmarks
## Development Guidelines
### 1. Getting Started
Before implementing process features:
1. Review [Getting Started Guide](getting_started.md)
2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Follow [Testing Guide](testing_guide.md)
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
5. Review [Events Package Specification](events_package_specification.md)
6. Review [Queue Package Specification](queue_package_specification.md)
### 2. Implementation Process
For each process feature:
1. Write tests following [Testing Guide](testing_guide.md)
2. Implement following Laravel patterns
3. Document following [Getting Started Guide](getting_started.md#documentation)
4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md)
### 3. Quality Requirements
All implementations must:
1. Pass all tests (see [Testing Guide](testing_guide.md))
2. Meet Laravel compatibility requirements
3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md))
4. Support event integration (see [Events Package Specification](events_package_specification.md))
5. Support queue integration (see [Queue Package Specification](queue_package_specification.md))
### 4. Integration Considerations
When implementing process features:
1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md)
2. Ensure Laravel compatibility per [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Use testing approaches from [Testing Guide](testing_guide.md)
4. Follow development setup in [Getting Started Guide](getting_started.md)
### 5. Performance Guidelines
Process system must:
1. Handle concurrent processes efficiently
2. Manage system resources
3. Support process pooling
4. Scale with process count
5. Meet performance targets in [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#performance-benchmarks)
### 6. Testing Requirements
Process tests must:
1. Cover all process operations
2. Test concurrent execution
3. Verify event handling
4. Check resource cleanup
5. Follow patterns in [Testing Guide](testing_guide.md)
### 7. Documentation Requirements
Process documentation must:
1. Explain process patterns
2. Show pool examples
3. Cover error handling
4. Include performance tips
5. Follow standards in [Getting Started Guide](getting_started.md#documentation)

301
docs/queue_gap_analysis.md Normal file
View file

@ -0,0 +1,301 @@
# Queue Package Gap Analysis
## Overview
This document analyzes the gaps between our Queue package's actual implementation and Laravel's queue functionality, identifying areas that need implementation or documentation updates.
> **Related Documentation**
> - See [Queue Package Specification](queue_package_specification.md) for current implementation
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for overall status
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Testing Guide](testing_guide.md) for testing approaches
> - See [Getting Started Guide](getting_started.md) for development setup
> - See [Events Package Specification](events_package_specification.md) for event integration
> - See [Bus Package Specification](bus_package_specification.md) for command bus integration
## Implementation Gaps
### 1. Missing Laravel Features
```dart
// Documented but not implemented:
// 1. Job Middleware
class JobMiddleware {
// Need to implement:
Future<void> handle(Job job, Function next);
Future<void> withoutOverlapping(Job job, Function next);
Future<void> rateLimit(Job job, Function next, int maxAttempts);
Future<void> throttle(Job job, Function next, Duration duration);
}
// 2. Job Events
class JobEvents {
// Need to implement:
void beforeJob(Job job);
void afterJob(Job job);
void failingJob(Job job, Exception exception);
void failedJob(Job job, Exception exception);
void retryingJob(Job job);
void retriedJob(Job job);
}
// 3. Job Chaining
class JobChain {
// Need to implement:
void chain(List<Job> jobs);
void onConnection(String connection);
void onQueue(String queue);
void catch(Function(Exception) handler);
void finally(Function handler);
}
```
### 2. Missing Queue Features
```dart
// Need to implement:
// 1. Queue Monitoring
class QueueMonitor {
// Need to implement:
Future<Map<String, int>> queueSizes();
Future<List<Map<String, dynamic>>> failedJobs();
Future<void> retryFailedJob(String id);
Future<void> forgetFailedJob(String id);
Future<void> pruneFailedJobs([Duration? older]);
}
// 2. Queue Rate Limiting
class QueueRateLimiter {
// Need to implement:
Future<bool> tooManyAttempts(String key, int maxAttempts);
Future<void> hit(String key, Duration decay);
Future<void> clear(String key);
Future<int> attempts(String key);
Future<int> remaining(String key, int maxAttempts);
}
// 3. Queue Batching
class QueueBatch {
// Need to implement:
Future<void> then(Function handler);
Future<void> catch(Function(Exception) handler);
Future<void> finally(Function handler);
Future<void> allowFailures();
Future<void> onConnection(String connection);
Future<void> onQueue(String queue);
}
```
### 3. Missing Worker Features
```dart
// Need to implement:
// 1. Worker Management
class WorkerManager {
// Need to implement:
Future<void> scale(int processes);
Future<void> pause();
Future<void> resume();
Future<void> restart();
Future<List<WorkerStatus>> status();
}
// 2. Worker Events
class WorkerEvents {
// Need to implement:
void workerStarting();
void workerStopping();
void workerStopped();
void queueEmpty(String queue);
void looping();
}
// 3. Worker Options
class WorkerOptions {
// Need to implement:
Duration sleep;
Duration timeout;
int maxTries;
int maxJobs;
bool force;
bool stopWhenEmpty;
bool rest;
}
```
## Documentation Gaps
### 1. Missing API Documentation
```dart
// Need to document:
/// Handles job middleware.
///
/// Example:
/// ```dart
/// class RateLimitedJob extends Job with JobMiddleware {
/// @override
/// Future<void> middleware(Function next) async {
/// return await rateLimit(5, Duration(minutes: 1), next);
/// }
/// }
/// ```
Future<void> middleware(Function next);
/// Handles job events.
///
/// Example:
/// ```dart
/// queue.beforeJob((job) {
/// print('Processing ${job.id}');
/// });
/// ```
void beforeJob(Function(Job) callback);
```
### 2. Missing Integration Examples
```dart
// Need examples for:
// 1. Job Chaining
var chain = queue.chain([
ProcessPodcast(podcast),
OptimizeAudio(podcast),
NotifySubscribers(podcast)
])
.onQueue('podcasts')
.catch((e) => handleError(e))
.finally(() => cleanup());
// 2. Queue Monitoring
var monitor = QueueMonitor(queue);
var sizes = await monitor.queueSizes();
print('Default queue size: ${sizes["default"]}');
// 3. Worker Management
var manager = WorkerManager(queue);
await manager.scale(4); // Scale to 4 processes
var status = await manager.status();
print('Active workers: ${status.length}');
```
### 3. Missing Test Coverage
```dart
// Need tests for:
void main() {
group('Job Middleware', () {
test('applies rate limiting', () async {
var job = RateLimitedJob();
var limiter = MockRateLimiter();
await job.handle();
verify(() => limiter.tooManyAttempts(any, any)).called(1);
});
});
group('Queue Monitoring', () {
test('monitors queue sizes', () async {
var monitor = QueueMonitor(queue);
var sizes = await monitor.queueSizes();
expect(sizes, containsPair('default', greaterThan(0)));
});
});
}
```
## Implementation Priority
1. **High Priority**
- Job middleware (Laravel compatibility)
- Job events (Laravel compatibility)
- Queue monitoring
2. **Medium Priority**
- Job chaining
- Queue rate limiting
- Worker management
3. **Low Priority**
- Additional worker features
- Additional monitoring features
- Performance optimizations
## Next Steps
1. **Implementation Tasks**
- Add job middleware
- Add job events
- Add queue monitoring
- Add worker management
2. **Documentation Tasks**
- Document job middleware
- Document job events
- Document monitoring
- Add integration examples
3. **Testing Tasks**
- Add middleware tests
- Add event tests
- Add monitoring tests
- Add worker tests
## Development Guidelines
### 1. Getting Started
Before implementing queue features:
1. Review [Getting Started Guide](getting_started.md)
2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Follow [Testing Guide](testing_guide.md)
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
5. Review [Queue Package Specification](queue_package_specification.md)
6. Review [Events Package Specification](events_package_specification.md)
7. Review [Bus Package Specification](bus_package_specification.md)
### 2. Implementation Process
For each queue feature:
1. Write tests following [Testing Guide](testing_guide.md)
2. Implement following Laravel patterns
3. Document following [Getting Started Guide](getting_started.md#documentation)
4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md)
### 3. Quality Requirements
All implementations must:
1. Pass all tests (see [Testing Guide](testing_guide.md))
2. Meet Laravel compatibility requirements
3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md))
4. Match specifications in [Queue Package Specification](queue_package_specification.md)
### 4. Integration Considerations
When implementing queue features:
1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md)
2. Ensure Laravel compatibility per [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Use testing approaches from [Testing Guide](testing_guide.md)
4. Follow development setup in [Getting Started Guide](getting_started.md)
### 5. Performance Guidelines
Queue system must:
1. Handle high job throughput
2. Process chains efficiently
3. Support concurrent workers
4. Scale horizontally
5. Meet performance targets in [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#performance-benchmarks)
### 6. Testing Requirements
Queue tests must:
1. Cover all queue operations
2. Test middleware behavior
3. Verify event handling
4. Check worker management
5. Follow patterns in [Testing Guide](testing_guide.md)
### 7. Documentation Requirements
Queue documentation must:
1. Explain queue patterns
2. Show middleware examples
3. Cover error handling
4. Include performance tips
5. Follow standards in [Getting Started Guide](getting_started.md#documentation)

View file

@ -0,0 +1,623 @@
# Queue Package Specification
## Overview
The Queue package provides a robust job queueing system that matches Laravel's queue functionality. It supports multiple queue drivers, job retries, rate limiting, and job batching while integrating with our Event and Bus packages.
> **Related Documentation**
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for implementation status
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Testing Guide](testing_guide.md) for testing approaches
> - See [Getting Started Guide](getting_started.md) for development setup
> - See [Events Package Specification](events_package_specification.md) for event integration
> - See [Bus Package Specification](bus_package_specification.md) for command bus integration
## Core Features
### 1. Queue Manager
```dart
/// Core queue manager implementation
class QueueManager implements QueueContract {
/// Available queue connections
final Map<String, QueueConnection> _connections = {};
/// Default connection name
final String _defaultConnection;
/// Configuration repository
final ConfigContract _config;
QueueManager(this._config)
: _defaultConnection = _config.get('queue.default', 'sync');
@override
Future<String> push(dynamic job, [String? queue]) async {
return await connection().push(job, queue);
}
@override
Future<String> later(Duration delay, dynamic job, [String? queue]) async {
return await connection().later(delay, job, queue);
}
@override
Future<Job?> pop([String? queue]) async {
return await connection().pop(queue);
}
@override
QueueConnection connection([String? name]) {
name ??= _defaultConnection;
return _connections.putIfAbsent(name, () {
var config = _getConfig(name!);
return _createConnection(config);
});
}
/// Creates a queue connection
QueueConnection _createConnection(Map<String, dynamic> config) {
switch (config['driver']) {
case 'sync':
return SyncConnection(config);
case 'database':
return DatabaseConnection(config);
case 'redis':
return RedisConnection(config);
case 'sqs':
return SqsConnection(config);
default:
throw UnsupportedError(
'Unsupported queue driver: ${config["driver"]}'
);
}
}
/// Gets connection config
Map<String, dynamic> _getConfig(String name) {
var config = _config.get<Map>('queue.connections.$name');
if (config == null) {
throw ArgumentError('Queue connection [$name] not configured.');
}
return config;
}
}
```
### 2. Queue Connections
```dart
/// Database queue connection
class DatabaseConnection implements QueueConnection {
/// Database connection
final DatabaseConnection _db;
/// Table name
final String _table;
DatabaseConnection(Map<String, dynamic> config)
: _db = DatabaseManager.connection(config['connection']),
_table = config['table'] ?? 'jobs';
@override
Future<String> push(dynamic job, [String? queue]) async {
queue ??= 'default';
var id = Uuid().v4();
await _db.table(_table).insert({
'id': id,
'queue': queue,
'payload': _serialize(job),
'attempts': 0,
'reserved_at': null,
'available_at': DateTime.now(),
'created_at': DateTime.now()
});
return id;
}
@override
Future<String> later(Duration delay, dynamic job, [String? queue]) async {
queue ??= 'default';
var id = Uuid().v4();
await _db.table(_table).insert({
'id': id,
'queue': queue,
'payload': _serialize(job),
'attempts': 0,
'reserved_at': null,
'available_at': DateTime.now().add(delay),
'created_at': DateTime.now()
});
return id;
}
@override
Future<Job?> pop([String? queue]) async {
queue ??= 'default';
var job = await _db.transaction((tx) async {
var job = await tx.table(_table)
.where('queue', queue)
.whereNull('reserved_at')
.where('available_at', '<=', DateTime.now())
.orderBy('id')
.first();
if (job != null) {
await tx.table(_table)
.where('id', job['id'])
.update({
'reserved_at': DateTime.now(),
'attempts': job['attempts'] + 1
});
}
return job;
});
if (job == null) return null;
return DatabaseJob(
connection: this,
queue: queue,
job: job
);
}
/// Serializes job payload
String _serialize(dynamic job) {
return jsonEncode({
'type': job.runtimeType.toString(),
'data': job.toMap()
});
}
}
/// Redis queue connection
class RedisConnection implements QueueConnection {
/// Redis client
final RedisClient _redis;
/// Key prefix
final String _prefix;
RedisConnection(Map<String, dynamic> config)
: _redis = RedisClient(
host: config['host'],
port: config['port'],
db: config['database']
),
_prefix = config['prefix'] ?? 'queues';
@override
Future<String> push(dynamic job, [String? queue]) async {
queue ??= 'default';
var id = Uuid().v4();
await _redis.rpush(
_getKey(queue),
_serialize(id, job)
);
return id;
}
@override
Future<String> later(Duration delay, dynamic job, [String? queue]) async {
queue ??= 'default';
var id = Uuid().v4();
await _redis.zadd(
_getDelayedKey(queue),
DateTime.now().add(delay).millisecondsSinceEpoch.toDouble(),
_serialize(id, job)
);
return id;
}
@override
Future<Job?> pop([String? queue]) async {
queue ??= 'default';
// Move delayed jobs
var now = DateTime.now().millisecondsSinceEpoch;
var jobs = await _redis.zrangebyscore(
_getDelayedKey(queue),
'-inf',
now.toString()
);
for (var job in jobs) {
await _redis.rpush(_getKey(queue), job);
await _redis.zrem(_getDelayedKey(queue), job);
}
// Get next job
var payload = await _redis.lpop(_getKey(queue));
if (payload == null) return null;
return RedisJob(
connection: this,
queue: queue,
payload: payload
);
}
/// Gets queue key
String _getKey(String queue) => '$_prefix:$queue';
/// Gets delayed queue key
String _getDelayedKey(String queue) => '$_prefix:$queue:delayed';
/// Serializes job payload
String _serialize(String id, dynamic job) {
return jsonEncode({
'id': id,
'type': job.runtimeType.toString(),
'data': job.toMap(),
'attempts': 0
});
}
}
```
### 3. Queue Jobs
```dart
/// Core job interface
abstract class Job {
/// Job ID
String get id;
/// Job queue
String get queue;
/// Number of attempts
int get attempts;
/// Maximum tries
int get maxTries => 3;
/// Timeout in seconds
int get timeout => 60;
/// Executes the job
Future<void> handle();
/// Releases the job back onto queue
Future<void> release([Duration? delay]);
/// Deletes the job
Future<void> delete();
/// Fails the job
Future<void> fail([Exception? exception]);
}
/// Database job implementation
class DatabaseJob implements Job {
/// Database connection
final DatabaseConnection _connection;
/// Job data
final Map<String, dynamic> _data;
/// Job queue
@override
final String queue;
DatabaseJob({
required DatabaseConnection connection,
required String queue,
required Map<String, dynamic> job
}) : _connection = connection,
_data = job,
queue = queue;
@override
String get id => _data['id'];
@override
int get attempts => _data['attempts'];
@override
Future<void> handle() async {
var payload = jsonDecode(_data['payload']);
var job = _deserialize(payload);
await job.handle();
}
@override
Future<void> release([Duration? delay]) async {
await _connection._db.table(_connection._table)
.where('id', id)
.update({
'reserved_at': null,
'available_at': delay != null
? DateTime.now().add(delay)
: DateTime.now()
});
}
@override
Future<void> delete() async {
await _connection._db.table(_connection._table)
.where('id', id)
.delete();
}
@override
Future<void> fail([Exception? exception]) async {
await _connection._db.table(_connection._table)
.where('id', id)
.update({
'failed_at': DateTime.now()
});
if (exception != null) {
await _connection._db.table('failed_jobs').insert({
'id': Uuid().v4(),
'connection': _connection.name,
'queue': queue,
'payload': _data['payload'],
'exception': exception.toString(),
'failed_at': DateTime.now()
});
}
}
/// Deserializes job payload
dynamic _deserialize(Map<String, dynamic> payload) {
var type = payload['type'];
var data = payload['data'];
return _connection._container.make(type)
..fromMap(data);
}
}
```
### 4. Job Batching
```dart
/// Job batch
class Batch {
/// Batch ID
final String id;
/// Queue connection
final QueueConnection _connection;
/// Jobs in batch
final List<Job> _jobs;
/// Options
final BatchOptions _options;
Batch(this.id, this._connection, this._jobs, this._options);
/// Gets total jobs
int get totalJobs => _jobs.length;
/// Gets pending jobs
Future<int> get pendingJobs async {
return await _connection.table('job_batches')
.where('id', id)
.value('pending_jobs');
}
/// Gets failed jobs
Future<int> get failedJobs async {
return await _connection.table('job_batches')
.where('id', id)
.value('failed_jobs');
}
/// Adds jobs to batch
Future<void> add(List<Job> jobs) async {
_jobs.addAll(jobs);
await _connection.table('job_batches')
.where('id', id)
.increment('total_jobs', jobs.length)
.increment('pending_jobs', jobs.length);
for (var job in jobs) {
await _connection.push(job);
}
}
/// Cancels the batch
Future<void> cancel() async {
await _connection.table('job_batches')
.where('id', id)
.update({
'cancelled_at': DateTime.now()
});
}
/// Deletes the batch
Future<void> delete() async {
await _connection.table('job_batches')
.where('id', id)
.delete();
}
}
```
## Integration Examples
### 1. Basic Queue Usage
```dart
// Define job
class ProcessPodcast implements Job {
final Podcast podcast;
@override
Future<void> handle() async {
await podcast.process();
}
}
// Push job to queue
await queue.push(ProcessPodcast(podcast));
// Push delayed job
await queue.later(
Duration(minutes: 10),
ProcessPodcast(podcast)
);
```
### 2. Job Batching
```dart
// Create batch
var batch = await queue.batch([
ProcessPodcast(podcast1),
ProcessPodcast(podcast2),
ProcessPodcast(podcast3)
])
.allowFailures()
.dispatch();
// Add more jobs
await batch.add([
ProcessPodcast(podcast4),
ProcessPodcast(podcast5)
]);
// Check progress
print('Pending: ${await batch.pendingJobs}');
print('Failed: ${await batch.failedJobs}');
```
### 3. Queue Worker
```dart
// Start worker
var worker = QueueWorker(connection)
..onJob((job) async {
print('Processing job ${job.id}');
})
..onException((job, exception) async {
print('Job ${job.id} failed: $exception');
});
await worker.daemon([
'default',
'emails',
'podcasts'
]);
```
## Testing
```dart
void main() {
group('Queue Manager', () {
test('pushes jobs to queue', () async {
var queue = QueueManager(config);
var job = ProcessPodcast(podcast);
var id = await queue.push(job);
expect(id, isNotEmpty);
verify(() => connection.push(job, null)).called(1);
});
test('handles delayed jobs', () async {
var queue = QueueManager(config);
var job = ProcessPodcast(podcast);
var delay = Duration(minutes: 5);
await queue.later(delay, job);
verify(() => connection.later(delay, job, null)).called(1);
});
});
group('Job Batching', () {
test('processes job batches', () async {
var batch = await queue.batch([
ProcessPodcast(podcast1),
ProcessPodcast(podcast2)
]).dispatch();
expect(batch.totalJobs, equals(2));
expect(await batch.pendingJobs, equals(2));
expect(await batch.failedJobs, equals(0));
});
});
}
```
## Next Steps
1. Implement core queue features
2. Add queue connections
3. Add job batching
4. Add queue worker
5. Write tests
6. Add benchmarks
## Development Guidelines
### 1. Getting Started
Before implementing queue features:
1. Review [Getting Started Guide](getting_started.md)
2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Follow [Testing Guide](testing_guide.md)
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
5. Review [Events Package Specification](events_package_specification.md)
6. Review [Bus Package Specification](bus_package_specification.md)
### 2. Implementation Process
For each queue feature:
1. Write tests following [Testing Guide](testing_guide.md)
2. Implement following Laravel patterns
3. Document following [Getting Started Guide](getting_started.md#documentation)
4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md)
### 3. Quality Requirements
All implementations must:
1. Pass all tests (see [Testing Guide](testing_guide.md))
2. Meet Laravel compatibility requirements
3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md))
4. Support event integration (see [Events Package Specification](events_package_specification.md))
5. Support bus integration (see [Bus Package Specification](bus_package_specification.md))
### 4. Integration Considerations
When implementing queue features:
1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md)
2. Ensure Laravel compatibility per [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Use testing approaches from [Testing Guide](testing_guide.md)
4. Follow development setup in [Getting Started Guide](getting_started.md)
### 5. Performance Guidelines
Queue system must:
1. Handle high job throughput
2. Process batches efficiently
3. Support concurrent workers
4. Scale horizontally
5. Meet performance targets in [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#performance-benchmarks)
### 6. Testing Requirements
Queue tests must:
1. Cover all queue operations
2. Test job processing
3. Verify batching
4. Check worker behavior
5. Follow patterns in [Testing Guide](testing_guide.md)
### 7. Documentation Requirements
Queue documentation must:
1. Explain queue patterns
2. Show job examples
3. Cover error handling
4. Include performance tips
5. Follow standards in [Getting Started Guide](getting_started.md#documentation)

295
docs/route_gap_analysis.md Normal file
View file

@ -0,0 +1,295 @@
# Route Package Gap Analysis
## Overview
This document analyzes the gaps between our Route package's actual implementation and Laravel's routing functionality, identifying areas that need implementation or documentation updates.
> **Related Documentation**
> - See [Route Package Specification](route_package_specification.md) for current implementation
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for overall status
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Testing Guide](testing_guide.md) for testing approaches
> - See [Getting Started Guide](getting_started.md) for development setup
> - See [Pipeline Package Specification](pipeline_package_specification.md) for middleware pipeline
> - See [Container Package Specification](container_package_specification.md) for dependency injection
## Implementation Gaps
### 1. Missing Laravel Features
```dart
// Documented but not implemented:
// 1. Route Model Binding
class RouteModelBinding {
// Need to implement:
void bind(String key, Type type);
void bindWhere(String key, Type type, Function where);
void bindCallback(String key, Function callback);
void scopeBindings();
}
// 2. Route Caching
class RouteCache {
// Need to implement:
Future<void> cache();
Future<void> clear();
bool isEnabled();
Future<void> reload();
Future<void> compile();
}
// 3. Route Fallbacks
class RouteFallback {
// Need to implement:
void fallback(dynamic action);
void missing(Function callback);
void methodNotAllowed(Function callback);
void notFound(Function callback);
}
```
### 2. Missing Route Features
```dart
// Need to implement:
// 1. Route Constraints
class RouteConstraints {
// Need to implement:
void pattern(String name, String pattern);
void patterns(Map<String, String> patterns);
bool matches(String name, String value);
void whereNumber(List<String> parameters);
void whereAlpha(List<String> parameters);
void whereAlphaNumeric(List<String> parameters);
void whereUuid(List<String> parameters);
}
// 2. Route Substitutions
class RouteSubstitution {
// Need to implement:
void substitute(String key, dynamic value);
void substituteBindings(Map<String, dynamic> bindings);
void substituteImplicit(Map<String, Type> bindings);
String resolveBinding(String key);
}
// 3. Route Rate Limiting
class RouteRateLimiting {
// Need to implement:
void throttle(String name, int maxAttempts, Duration decay);
void rateLimit(String name, int maxAttempts, Duration decay);
void forUser(String name, int maxAttempts, Duration decay);
void exempt(List<String> routes);
}
```
### 3. Missing Group Features
```dart
// Need to implement:
// 1. Group Attributes
class RouteGroupAttributes {
// Need to implement:
void controller(Type controller);
void namespace(String namespace);
void name(String name);
void domain(String domain);
void where(Map<String, String> patterns);
}
// 2. Group Middleware
class RouteGroupMiddleware {
// Need to implement:
void aliasMiddleware(String name, Type middleware);
void middlewarePriority(List<String> middleware);
void pushMiddlewareToGroup(String group, String middleware);
List<String> getMiddlewareGroups();
}
// 3. Group Resources
class RouteGroupResources {
// Need to implement:
void resources(Map<String, Type> resources);
void apiResources(Map<String, Type> resources);
void singleton(String name, Type controller);
void apiSingleton(String name, Type controller);
}
```
## Documentation Gaps
### 1. Missing API Documentation
```dart
// Need to document:
/// Binds route model.
///
/// Example:
/// ```dart
/// router.bind('user', User, (value) async {
/// return await User.find(value);
/// });
/// ```
void bind(String key, Type type, [Function? callback]);
/// Defines route pattern.
///
/// Example:
/// ```dart
/// router.pattern('id', '[0-9]+');
/// router.get('users/{id}', UsersController)
/// .where('id', '[0-9]+');
/// ```
void pattern(String name, String pattern);
```
### 2. Missing Integration Examples
```dart
// Need examples for:
// 1. Route Model Binding
router.bind('user', User);
router.get('users/{user}', (User user) {
return user; // Auto-resolved from ID
});
// 2. Route Rate Limiting
router.middleware(['throttle:60,1'])
.group(() {
router.get('api/users', UsersController);
});
// 3. Route Resources
router.resources({
'photos': PhotoController,
'posts': PostController
});
```
### 3. Missing Test Coverage
```dart
// Need tests for:
void main() {
group('Route Model Binding', () {
test('resolves bound models', () async {
router.bind('user', User);
var response = await router.dispatch(
Request('GET', '/users/1')
);
expect(response.data, isA<User>());
expect(response.data.id, equals('1'));
});
});
group('Route Caching', () {
test('caches compiled routes', () async {
await router.cache();
var route = router.match(
Request('GET', '/users/1')
);
expect(route, isNotNull);
expect(route!.compiled, isTrue);
});
});
}
```
## Implementation Priority
1. **High Priority**
- Route model binding (Laravel compatibility)
- Route caching (Laravel compatibility)
- Route constraints
2. **Medium Priority**
- Route substitutions
- Route rate limiting
- Group resources
3. **Low Priority**
- Route fallbacks
- Additional constraints
- Performance optimizations
## Next Steps
1. **Implementation Tasks**
- Add route model binding
- Add route caching
- Add route constraints
- Add group resources
2. **Documentation Tasks**
- Document model binding
- Document caching
- Document constraints
- Add integration examples
3. **Testing Tasks**
- Add binding tests
- Add caching tests
- Add constraint tests
- Add resource tests
## Development Guidelines
### 1. Getting Started
Before implementing routing features:
1. Review [Getting Started Guide](getting_started.md)
2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Follow [Testing Guide](testing_guide.md)
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
5. Review [Route Package Specification](route_package_specification.md)
6. Review [Pipeline Package Specification](pipeline_package_specification.md)
7. Review [Container Package Specification](container_package_specification.md)
### 2. Implementation Process
For each routing feature:
1. Write tests following [Testing Guide](testing_guide.md)
2. Implement following Laravel patterns
3. Document following [Getting Started Guide](getting_started.md#documentation)
4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md)
### 3. Quality Requirements
All implementations must:
1. Pass all tests (see [Testing Guide](testing_guide.md))
2. Meet Laravel compatibility requirements
3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md))
4. Match specifications in [Route Package Specification](route_package_specification.md)
### 4. Integration Considerations
When implementing routing features:
1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md)
2. Ensure Laravel compatibility per [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Use testing approaches from [Testing Guide](testing_guide.md)
4. Follow development setup in [Getting Started Guide](getting_started.md)
### 5. Performance Guidelines
Routing system must:
1. Match routes efficiently
2. Handle complex patterns
3. Support caching
4. Scale with route count
5. Meet performance targets in [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#performance-benchmarks)
### 6. Testing Requirements
Route tests must:
1. Cover all route types
2. Test pattern matching
3. Verify middleware
4. Check parameter binding
5. Follow patterns in [Testing Guide](testing_guide.md)
### 7. Documentation Requirements
Route documentation must:
1. Explain routing patterns
2. Show group examples
3. Cover parameter binding
4. Include performance tips
5. Follow standards in [Getting Started Guide](getting_started.md#documentation)

View file

@ -0,0 +1,550 @@
# Route Package Specification
## Overview
The Route package provides a robust routing system that matches Laravel's routing functionality. It supports route registration, middleware, parameter binding, and route groups while integrating with our Pipeline and Container packages.
> **Related Documentation**
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for implementation status
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Testing Guide](testing_guide.md) for testing approaches
> - See [Getting Started Guide](getting_started.md) for development setup
> - See [Pipeline Package Specification](pipeline_package_specification.md) for middleware pipeline
> - See [Container Package Specification](container_package_specification.md) for dependency injection
## Core Features
### 1. Router
```dart
/// Core router implementation
class Router implements RouterContract {
/// Container instance
final Container _container;
/// Route collection
final RouteCollection _routes;
/// Current route
Route? _current;
/// Global middleware
final List<dynamic> _middleware = [];
Router(this._container)
: _routes = RouteCollection();
/// Gets current route
Route? get current => _current;
/// Gets global middleware
List<dynamic> get middleware => List.from(_middleware);
/// Adds global middleware
void pushMiddleware(dynamic middleware) {
_middleware.add(middleware);
}
/// Registers GET route
Route get(String uri, dynamic action) {
return addRoute(['GET', 'HEAD'], uri, action);
}
/// Registers POST route
Route post(String uri, dynamic action) {
return addRoute(['POST'], uri, action);
}
/// Registers PUT route
Route put(String uri, dynamic action) {
return addRoute(['PUT'], uri, action);
}
/// Registers DELETE route
Route delete(String uri, dynamic action) {
return addRoute(['DELETE'], uri, action);
}
/// Registers PATCH route
Route patch(String uri, dynamic action) {
return addRoute(['PATCH'], uri, action);
}
/// Registers OPTIONS route
Route options(String uri, dynamic action) {
return addRoute(['OPTIONS'], uri, action);
}
/// Adds route to collection
Route addRoute(List<String> methods, String uri, dynamic action) {
var route = Route(methods, uri, action);
_routes.add(route);
return route;
}
/// Creates route group
void group(Map<String, dynamic> attributes, Function callback) {
var group = RouteGroup(attributes);
_routes.pushGroup(group);
callback();
_routes.popGroup();
}
/// Matches request to route
Route? match(Request request) {
_current = _routes.match(request);
return _current;
}
/// Dispatches request to route
Future<Response> dispatch(Request request) async {
var route = match(request);
if (route == null) {
throw RouteNotFoundException();
}
return await _runRoute(route, request);
}
/// Runs route through middleware
Future<Response> _runRoute(Route route, Request request) async {
var pipeline = _container.make<MiddlewarePipeline>();
return await pipeline
.send(request)
.through([
..._middleware,
...route.gatherMiddleware()
])
.then((request) => route.run(request));
}
}
```
### 2. Route Collection
```dart
/// Route collection
class RouteCollection {
/// Routes by method
final Map<String, List<Route>> _routes = {};
/// Route groups
final List<RouteGroup> _groups = [];
/// Adds route to collection
void add(Route route) {
for (var method in route.methods) {
_routes.putIfAbsent(method, () => []).add(route);
}
if (_groups.isNotEmpty) {
route.group = _groups.last;
}
}
/// Pushes route group
void pushGroup(RouteGroup group) {
if (_groups.isNotEmpty) {
group.parent = _groups.last;
}
_groups.add(group);
}
/// Pops route group
void popGroup() {
_groups.removeLast();
}
/// Matches request to route
Route? match(Request request) {
var routes = _routes[request.method] ?? [];
for (var route in routes) {
if (route.matches(request)) {
return route;
}
}
return null;
}
}
```
### 3. Route
```dart
/// Route definition
class Route {
/// HTTP methods
final List<String> methods;
/// URI pattern
final String uri;
/// Route action
final dynamic action;
/// Route group
RouteGroup? group;
/// Route middleware
final List<dynamic> _middleware = [];
/// Route parameters
final Map<String, dynamic> _parameters = {};
Route(this.methods, this.uri, this.action);
/// Adds middleware
Route middleware(List<dynamic> middleware) {
_middleware.addAll(middleware);
return this;
}
/// Gets route name
String? get name => _parameters['as'];
/// Sets route name
Route name(String name) {
_parameters['as'] = name;
return this;
}
/// Gets route domain
String? get domain => _parameters['domain'];
/// Sets route domain
Route domain(String domain) {
_parameters['domain'] = domain;
return this;
}
/// Gets route prefix
String get prefix {
var prefix = '';
var group = this.group;
while (group != null) {
if (group.prefix != null) {
prefix = '${group.prefix}/$prefix';
}
group = group.parent;
}
return prefix.isEmpty ? '' : prefix;
}
/// Gets full URI
String get fullUri => '${prefix.isEmpty ? "" : "$prefix/"}$uri';
/// Gathers middleware
List<dynamic> gatherMiddleware() {
var middleware = [..._middleware];
var group = this.group;
while (group != null) {
middleware.addAll(group.middleware);
group = group.parent;
}
return middleware;
}
/// Matches request
bool matches(Request request) {
return _matchesMethod(request.method) &&
_matchesUri(request.uri) &&
_matchesDomain(request.host);
}
/// Matches HTTP method
bool _matchesMethod(String method) {
return methods.contains(method);
}
/// Matches URI pattern
bool _matchesUri(Uri uri) {
var pattern = RegExp(_compilePattern());
return pattern.hasMatch(uri.path);
}
/// Matches domain pattern
bool _matchesDomain(String? host) {
if (domain == null) return true;
if (host == null) return false;
var pattern = RegExp(_compileDomainPattern());
return pattern.hasMatch(host);
}
/// Compiles URI pattern
String _compilePattern() {
return fullUri
.replaceAll('/', '\\/')
.replaceAllMapped(
RegExp(r'{([^}]+)}'),
(match) => '(?<${match[1]}>[^/]+)'
);
}
/// Compiles domain pattern
String _compileDomainPattern() {
return domain!
.replaceAll('.', '\\.')
.replaceAllMapped(
RegExp(r'{([^}]+)}'),
(match) => '(?<${match[1]}>[^.]+)'
);
}
/// Runs route action
Future<Response> run(Request request) async {
var action = _resolveAction();
var parameters = _resolveParameters(request);
if (action is Function) {
return await Function.apply(action, parameters);
}
if (action is Controller) {
return await action.callAction(
action.runtimeType.toString(),
parameters
);
}
throw RouteActionNotFoundException();
}
/// Resolves route action
dynamic _resolveAction() {
if (action is String) {
var parts = action.split('@');
var controller = _container.make(parts[0]);
controller.method = parts[1];
return controller;
}
return action;
}
/// Resolves route parameters
List<dynamic> _resolveParameters(Request request) {
var pattern = RegExp(_compilePattern());
var match = pattern.firstMatch(request.uri.path);
if (match == null) return [];
return match.groupNames.map((name) {
return _resolveParameter(name, match.namedGroup(name)!);
}).toList();
}
/// Resolves route parameter
dynamic _resolveParameter(String name, String value) {
if (_parameters.containsKey(name)) {
return _parameters[name](value);
}
return value;
}
}
```
### 4. Route Groups
```dart
/// Route group
class RouteGroup {
/// Group attributes
final Map<String, dynamic> attributes;
/// Parent group
RouteGroup? parent;
RouteGroup(this.attributes);
/// Gets group prefix
String? get prefix => attributes['prefix'];
/// Gets group middleware
List<dynamic> get middleware => attributes['middleware'] ?? [];
/// Gets group domain
String? get domain => attributes['domain'];
/// Gets group name prefix
String? get namePrefix => attributes['as'];
/// Gets merged attributes
Map<String, dynamic> get mergedAttributes {
var merged = Map.from(attributes);
var parent = this.parent;
while (parent != null) {
for (var entry in parent.attributes.entries) {
if (!merged.containsKey(entry.key)) {
merged[entry.key] = entry.value;
}
}
parent = parent.parent;
}
return merged;
}
}
```
## Integration Examples
### 1. Basic Routing
```dart
// Register routes
router.get('/', HomeController);
router.post('/users', UsersController);
router.get('/users/{id}', (String id) {
return User.find(id);
});
// Match and dispatch
var route = router.match(request);
var response = await router.dispatch(request);
```
### 2. Route Groups
```dart
router.group({
'prefix': 'api',
'middleware': ['auth'],
'namespace': 'Api'
}, () {
router.get('users', UsersController);
router.get('posts', PostsController);
router.group({
'prefix': 'admin',
'middleware': ['admin']
}, () {
router.get('stats', StatsController);
});
});
```
### 3. Route Parameters
```dart
// Required parameters
router.get('users/{id}', (String id) {
return User.find(id);
});
// Optional parameters
router.get('posts/{id?}', (String? id) {
return id != null ? Post.find(id) : Post.all();
});
// Regular expression constraints
router.get('users/{id}', UsersController)
.where('id', '[0-9]+');
```
## Testing
```dart
void main() {
group('Router', () {
test('matches routes', () {
var router = Router(container);
router.get('/users/{id}', UsersController);
var request = Request('GET', '/users/1');
var route = router.match(request);
expect(route, isNotNull);
expect(route!.action, equals(UsersController));
});
test('handles route groups', () {
var router = Router(container);
router.group({
'prefix': 'api',
'middleware': ['auth']
}, () {
router.get('users', UsersController);
});
var route = router.match(Request('GET', '/api/users'));
expect(route, isNotNull);
expect(route!.gatherMiddleware(), contains('auth'));
});
});
}
```
## Next Steps
1. Implement core routing
2. Add route groups
3. Add route parameters
4. Add middleware support
5. Write tests
6. Add benchmarks
## Development Guidelines
### 1. Getting Started
Before implementing routing features:
1. Review [Getting Started Guide](getting_started.md)
2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Follow [Testing Guide](testing_guide.md)
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
5. Review [Pipeline Package Specification](pipeline_package_specification.md)
6. Review [Container Package Specification](container_package_specification.md)
### 2. Implementation Process
For each routing feature:
1. Write tests following [Testing Guide](testing_guide.md)
2. Implement following Laravel patterns
3. Document following [Getting Started Guide](getting_started.md#documentation)
4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md)
### 3. Quality Requirements
All implementations must:
1. Pass all tests (see [Testing Guide](testing_guide.md))
2. Meet Laravel compatibility requirements
3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md))
4. Support middleware (see [Pipeline Package Specification](pipeline_package_specification.md))
5. Support dependency injection (see [Container Package Specification](container_package_specification.md))
### 4. Integration Considerations
When implementing routing features:
1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md)
2. Ensure Laravel compatibility per [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Use testing approaches from [Testing Guide](testing_guide.md)
4. Follow development setup in [Getting Started Guide](getting_started.md)
### 5. Performance Guidelines
Routing system must:
1. Match routes efficiently
2. Handle complex patterns
3. Support caching
4. Scale with route count
5. Meet performance targets in [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#performance-benchmarks)
### 6. Testing Requirements
Route tests must:
1. Cover all route types
2. Test pattern matching
3. Verify middleware
4. Check parameter binding
5. Follow patterns in [Testing Guide](testing_guide.md)
### 7. Documentation Requirements
Route documentation must:
1. Explain routing patterns
2. Show group examples
3. Cover parameter binding
4. Include performance tips
5. Follow standards in [Getting Started Guide](getting_started.md#documentation)

View file

@ -0,0 +1,436 @@
# Support Package Specification
## Overview
The Support package provides fundamental utilities, helper functions, and common abstractions used throughout the framework. It aims to match Laravel's Support package functionality while leveraging Dart's strengths.
> **Related Documentation**
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for implementation status
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Testing Guide](testing_guide.md) for testing approaches
> - See [Getting Started Guide](getting_started.md) for development setup
> - See [Contracts Package Specification](contracts_package_specification.md) for support contracts
## Core Features
### 1. Collections
```dart
/// Provides Laravel-like collection operations
class Collection<T> {
final List<T> _items;
Collection(this._items);
/// Creates a collection from an iterable
factory Collection.from(Iterable<T> items) {
return Collection(items.toList());
}
/// Maps items while maintaining collection type
Collection<R> map<R>(R Function(T) callback) {
return Collection(_items.map(callback).toList());
}
/// Filters items
Collection<T> where(bool Function(T) test) {
return Collection(_items.where(test).toList());
}
/// Reduces collection to single value
R reduce<R>(R Function(R, T) callback, R initial) {
return _items.fold(initial, callback);
}
/// Groups items by key
Map<K, List<T>> groupBy<K>(K Function(T) keySelector) {
return _items.fold({}, (map, item) {
var key = keySelector(item);
map.putIfAbsent(key, () => []).add(item);
return map;
});
}
}
```
### 2. String Manipulation
```dart
/// Provides Laravel-like string manipulation
extension StringHelpers on String {
/// Converts string to camelCase
String camelCase() {
var words = split(RegExp(r'[\s_-]+'));
return words.first +
words.skip(1)
.map((w) => w[0].toUpperCase() + w.substring(1))
.join();
}
/// Converts string to snake_case
String snakeCase() {
return replaceAllMapped(
RegExp(r'[A-Z]'),
(m) => '_${m[0]!.toLowerCase()}'
).replaceAll(RegExp(r'^_'), '');
}
/// Converts string to kebab-case
String kebabCase() {
return snakeCase().replaceAll('_', '-');
}
/// Converts string to StudlyCase
String studlyCase() {
return split(RegExp(r'[\s_-]+'))
.map((w) => w[0].toUpperCase() + w.substring(1))
.join();
}
}
```
### 3. Array/List Helpers
```dart
/// Provides Laravel-like array manipulation
extension ArrayHelpers<T> on List<T> {
/// Gets first item matching predicate
T? firstWhere(bool Function(T) test, {T? orElse()}) {
try {
return super.firstWhere(test);
} catch (e) {
return orElse?.call();
}
}
/// Plucks single field from list of maps
List<V> pluck<V>(String key) {
return map((item) =>
(item as Map<String, dynamic>)[key] as V
).toList();
}
/// Groups items by key
Map<K, List<T>> groupBy<K>(K Function(T) keySelector) {
return fold({}, (map, item) {
var key = keySelector(item);
map.putIfAbsent(key, () => []).add(item);
return map;
});
}
}
```
### 4. Service Provider Support
```dart
/// Base class for service providers
abstract class ServiceProvider {
/// The container instance
late final Container container;
/// Register bindings with the container
void register();
/// Bootstrap any application services
void boot() {}
/// Determines if provider is deferred
bool get isDeferred => false;
/// Gets services provided
List<Type> get provides => [];
}
/// Marks a provider as deferred
abstract class DeferredServiceProvider extends ServiceProvider {
@override
bool get isDeferred => true;
/// Gets events that trigger loading
List<String> get when => [];
}
```
### 5. Fluent Interface
```dart
/// Provides fluent interface building
class Fluent {
final Map<String, dynamic> _attributes;
Fluent([Map<String, dynamic>? attributes])
: _attributes = attributes ?? {};
/// Gets attribute value
T? get<T>(String key) => _attributes[key] as T?;
/// Sets attribute value
Fluent set(String key, dynamic value) {
_attributes[key] = value;
return this;
}
/// Gets all attributes
Map<String, dynamic> toMap() => Map.from(_attributes);
}
```
### 6. Optional Type
```dart
/// Provides Laravel-like Optional type
class Optional<T> {
final T? _value;
const Optional(this._value);
/// Creates Optional from nullable value
factory Optional.of(T? value) => Optional(value);
/// Gets value or default
T get(T defaultValue) => _value ?? defaultValue;
/// Maps value if present
Optional<R> map<R>(R Function(T) mapper) {
return Optional(_value == null ? null : mapper(_value!));
}
/// Returns true if value is present
bool get isPresent => _value != null;
}
```
### 7. High Order Message Proxies
```dart
/// Provides Laravel-like high order messaging
class HigherOrderProxy<T> {
final T _target;
HigherOrderProxy(this._target);
/// Invokes method on target
R call<R>(String method, [List<dynamic>? args]) {
return Function.apply(
_target.runtimeType.getMethod(method),
args ?? []
) as R;
}
}
```
## Integration with Container
```dart
/// Register support services
class SupportServiceProvider extends ServiceProvider {
@override
void register() {
// Register collection factory
container.bind<CollectionFactory>((c) => CollectionFactory());
// Register string helpers
container.bind<StringHelpers>((c) => StringHelpers());
// Register array helpers
container.bind<ArrayHelpers>((c) => ArrayHelpers());
}
}
```
## Usage Examples
### Collections
```dart
// Create collection
var collection = Collection([1, 2, 3, 4, 5]);
// Chain operations
var result = collection
.where((n) => n.isEven)
.map((n) => n * 2)
.reduce((sum, n) => sum + n, 0);
// Group items
var users = Collection([
User('John', 'Admin'),
User('Jane', 'User'),
User('Bob', 'Admin')
]);
var byRole = users.groupBy((u) => u.role);
```
### String Helpers
```dart
// Convert cases
'user_name'.camelCase(); // userName
'userName'.snakeCase(); // user_name
'user name'.studlyCase(); // UserName
'UserName'.kebabCase(); // user-name
// Other operations
'hello'.padLeft(10); // ' hello'
'HELLO'.toLowerCase(); // 'hello'
' text '.trim(); // 'text'
```
### Service Providers
```dart
class UserServiceProvider extends ServiceProvider {
@override
void register() {
container.bind<UserRepository>((c) => UserRepositoryImpl());
}
@override
void boot() {
var repo = container.make<UserRepository>();
repo.initialize();
}
}
class CacheServiceProvider extends DeferredServiceProvider {
@override
List<String> get when => ['cache.needed'];
@override
List<Type> get provides => [CacheManager];
@override
void register() {
container.bind<CacheManager>((c) => CacheManagerImpl());
}
}
```
## Testing
```dart
void main() {
group('Collection Tests', () {
test('should map values', () {
var collection = Collection([1, 2, 3]);
var result = collection.map((n) => n * 2);
expect(result.toList(), equals([2, 4, 6]));
});
test('should filter values', () {
var collection = Collection([1, 2, 3, 4]);
var result = collection.where((n) => n.isEven);
expect(result.toList(), equals([2, 4]));
});
});
group('String Helper Tests', () {
test('should convert to camelCase', () {
expect('user_name'.camelCase(), equals('userName'));
expect('first name'.camelCase(), equals('firstName'));
});
test('should convert to snakeCase', () {
expect('userName'.snakeCase(), equals('user_name'));
expect('FirstName'.snakeCase(), equals('first_name'));
});
});
}
```
## Performance Considerations
1. **Collection Operations**
```dart
// Use lazy evaluation when possible
collection
.where((n) => n.isEven) // Lazy
.map((n) => n * 2) // Lazy
.toList(); // Eager
```
2. **String Manipulations**
```dart
// Cache regex patterns
final _camelCasePattern = RegExp(r'[\s_-]+');
final _snakeCasePattern = RegExp(r'[A-Z]');
// Use StringBuffer for concatenation
final buffer = StringBuffer();
for (var word in words) {
buffer.write(word);
}
```
3. **Service Provider Loading**
```dart
// Defer provider loading when possible
class HeavyServiceProvider extends DeferredServiceProvider {
@override
List<String> get when => ['heavy.needed'];
}
```
## Next Steps
1. Implement core features
2. Add comprehensive tests
3. Create integration examples
4. Add performance benchmarks
Would you like me to continue with documentation for the Pipeline or Contracts package?
## Development Guidelines
### 1. Getting Started
Before implementing support features:
1. Review [Getting Started Guide](getting_started.md)
2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Follow [Testing Guide](testing_guide.md)
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
5. Understand [Contracts Package Specification](contracts_package_specification.md)
### 2. Implementation Process
For each support feature:
1. Write tests following [Testing Guide](testing_guide.md)
2. Implement following Laravel patterns
3. Document following [Getting Started Guide](getting_started.md#documentation)
4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md)
### 3. Quality Requirements
All implementations must:
1. Pass all tests (see [Testing Guide](testing_guide.md))
2. Meet Laravel compatibility requirements
3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md))
4. Implement required contracts (see [Contracts Package Specification](contracts_package_specification.md))
### 4. Integration Considerations
When implementing support features:
1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md)
2. Ensure Laravel compatibility per [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Use testing approaches from [Testing Guide](testing_guide.md)
4. Follow development setup in [Getting Started Guide](getting_started.md)
5. Implement all contracts from [Contracts Package Specification](contracts_package_specification.md)
### 5. Performance Guidelines
Support utilities must:
1. Handle large collections efficiently
2. Optimize string operations
3. Minimize memory allocations
4. Support async operations where appropriate
5. Meet performance targets in [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#performance-benchmarks)
### 6. Testing Requirements
Support tests must:
1. Cover all utility functions
2. Test edge cases
3. Verify error handling
4. Check performance characteristics
5. Follow patterns in [Testing Guide](testing_guide.md)
### 7. Documentation Requirements
Support documentation must:
1. Explain utility patterns
2. Show usage examples
3. Cover error handling
4. Include performance tips
5. Follow standards in [Getting Started Guide](getting_started.md#documentation)

View file

@ -1,10 +0,0 @@
# Performance Testing
The performance test can be run with the following tools.
## WRT
```bash
wrk -t12 -c400 -d30s http://localhost:8080/query?queries=20
```
This runs a benchmark for 30 seconds, using 12 threads, and keeping 400 HTTP connections open.

View file

@ -0,0 +1,312 @@
# Testing Package Gap Analysis
## Overview
This document analyzes the gaps between our Testing package's actual implementation and Laravel's testing functionality, identifying areas that need implementation or documentation updates.
> **Related Documentation**
> - See [Testing Package Specification](testing_package_specification.md) for current implementation
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for overall status
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Testing Guide](testing_guide.md) for testing approaches
> - See [Getting Started Guide](getting_started.md) for development setup
> - See [Container Package Specification](container_package_specification.md) for dependency injection
> - See [Events Package Specification](events_package_specification.md) for event testing
## Implementation Gaps
### 1. Missing Laravel Features
```dart
// Documented but not implemented:
// 1. Browser Testing
class BrowserTest {
// Need to implement:
Future<void> browse(Function(Browser) callback);
Future<void> visit(String page);
Future<void> click(String text);
Future<void> type(String field, String value);
Future<void> press(String button);
Future<void> assertSee(String text);
Future<void> assertPathIs(String path);
}
// 2. Parallel Testing
class ParallelTesting {
// Need to implement:
void setToken(String token);
void setProcesses(int count);
Future<void> runInParallel();
Future<void> withoutOverlapping(String key);
Future<void> isolateDatabase();
}
// 3. Time Testing
class TimeTesting {
// Need to implement:
void travel(Duration duration);
void freeze(DateTime time);
void resume();
void setTestNow(DateTime time);
DateTime now();
}
```
### 2. Missing Test Features
```dart
// Need to implement:
// 1. Test Data Factories
class TestDataFactory<T> {
// Need to implement:
T define(Map<String, dynamic> attributes);
T make([Map<String, dynamic>? attributes]);
Future<T> create([Map<String, dynamic>? attributes]);
List<T> makeMany(int count, [Map<String, dynamic>? attributes]);
Future<List<T>> createMany(int count, [Map<String, dynamic>? attributes]);
}
// 2. Test Doubles
class TestDoubles {
// Need to implement:
dynamic spy(dynamic target);
dynamic mock(Type type);
dynamic fake(Type type);
dynamic partial(Type type);
void verifyNever(Function invocation);
void verifyOnce(Function invocation);
}
// 3. Test Database
class TestDatabase {
// Need to implement:
Future<void> beginTransaction();
Future<void> rollback();
Future<void> refresh();
Future<void> seed(String class);
Future<void> truncate(List<String> tables);
}
```
### 3. Missing Assertion Features
```dart
// Need to implement:
// 1. Collection Assertions
class CollectionAssertions {
// Need to implement:
void assertCount(int count);
void assertEmpty();
void assertContains(dynamic item);
void assertDoesntContain(dynamic item);
void assertHasKey(String key);
void assertHasValue(dynamic value);
}
// 2. Response Assertions
class ResponseAssertions {
// Need to implement:
void assertViewIs(String name);
void assertViewHas(String key, [dynamic value]);
void assertViewMissing(String key);
void assertSessionHas(String key, [dynamic value]);
void assertSessionMissing(String key);
void assertCookie(String name, [String? value]);
}
// 3. Exception Assertions
class ExceptionAssertions {
// Need to implement:
void assertThrows<T>(Function callback);
void assertDoesntThrow(Function callback);
void assertThrowsMessage(Type type, String message);
void assertThrowsIf(bool condition, Function callback);
}
```
## Documentation Gaps
### 1. Missing API Documentation
```dart
// Need to document:
/// Runs browser test.
///
/// Example:
/// ```dart
/// await browse((browser) async {
/// await browser.visit('/login');
/// await browser.type('email', 'user@example.com');
/// await browser.press('Login');
/// await browser.assertPathIs('/dashboard');
/// });
/// ```
Future<void> browse(Function(Browser) callback);
/// Creates test data factory.
///
/// Example:
/// ```dart
/// class UserFactory extends Factory<User> {
/// @override
/// User define() {
/// return User()
/// ..name = faker.person.name()
/// ..email = faker.internet.email();
/// }
/// }
/// ```
abstract class Factory<T>;
```
### 2. Missing Integration Examples
```dart
// Need examples for:
// 1. Parallel Testing
await test.parallel((runner) {
runner.setProcesses(4);
runner.isolateDatabase();
await runner.run();
});
// 2. Time Testing
await test.freeze(DateTime(2024, 1, 1), () async {
await processScheduledJobs();
await test.travel(Duration(days: 1));
await verifyJobsCompleted();
});
// 3. Test Doubles
var mock = test.mock(PaymentGateway);
when(mock.charge(any)).thenReturn(true);
await processPayment(mock);
verify(mock.charge(any)).called(1);
```
### 3. Missing Test Coverage
```dart
// Need tests for:
void main() {
group('Browser Testing', () {
test('interacts with browser', () async {
await browse((browser) async {
await browser.visit('/login');
await browser.type('email', 'test@example.com');
await browser.type('password', 'password');
await browser.press('Login');
await browser.assertPathIs('/dashboard');
await browser.assertSee('Welcome');
});
});
});
group('Test Factories', () {
test('creates test data', () async {
var users = await UserFactory()
.count(3)
.create();
expect(users, hasLength(3));
expect(users.first.email, contains('@'));
});
});
}
```
## Implementation Priority
1. **High Priority**
- Browser testing (Laravel compatibility)
- Parallel testing (Laravel compatibility)
- Test data factories
2. **Medium Priority**
- Test doubles
- Time testing
- Test database features
3. **Low Priority**
- Additional assertions
- Additional test helpers
- Performance optimizations
## Next Steps
1. **Implementation Tasks**
- Add browser testing
- Add parallel testing
- Add test factories
- Add test doubles
2. **Documentation Tasks**
- Document browser testing
- Document parallel testing
- Document factories
- Add integration examples
3. **Testing Tasks**
- Add browser tests
- Add parallel tests
- Add factory tests
- Add double tests
## Development Guidelines
### 1. Getting Started
Before implementing testing features:
1. Review [Getting Started Guide](getting_started.md)
2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Follow [Testing Guide](testing_guide.md)
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
5. Review [Testing Package Specification](testing_package_specification.md)
6. Review [Container Package Specification](container_package_specification.md)
7. Review [Events Package Specification](events_package_specification.md)
### 2. Implementation Process
For each testing feature:
1. Write tests following [Testing Guide](testing_guide.md)
2. Implement following Laravel patterns
3. Document following [Getting Started Guide](getting_started.md#documentation)
4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md)
### 3. Quality Requirements
All implementations must:
1. Pass all tests (see [Testing Guide](testing_guide.md))
2. Meet Laravel compatibility requirements
3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md))
4. Match specifications in [Testing Package Specification](testing_package_specification.md)
### 4. Integration Considerations
When implementing testing features:
1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md)
2. Ensure Laravel compatibility per [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Use testing approaches from [Testing Guide](testing_guide.md)
4. Follow development setup in [Getting Started Guide](getting_started.md)
### 5. Performance Guidelines
Testing system must:
1. Execute tests efficiently
2. Support parallel testing
3. Handle large test suites
4. Manage test isolation
5. Meet performance targets in [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#performance-benchmarks)
### 6. Testing Requirements
Testing package tests must:
1. Cover all testing features
2. Test browser interactions
3. Verify parallel execution
4. Check test factories
5. Follow patterns in [Testing Guide](testing_guide.md)
### 7. Documentation Requirements
Testing documentation must:
1. Explain testing patterns
2. Show browser examples
3. Cover parallel testing
4. Include performance tips
5. Follow standards in [Getting Started Guide](getting_started.md#documentation)

343
docs/testing_guide.md Normal file
View file

@ -0,0 +1,343 @@
# Testing Guide
## Overview
This guide outlines our testing approach, which follows Laravel's testing patterns while leveraging Dart's testing capabilities. It covers unit testing, integration testing, performance testing, and Laravel-style testing approaches.
## Test Types
### 1. Unit Tests
```dart
void main() {
group('Service Tests', () {
late Container container;
late UserService service;
setUp(() {
container = Container(reflector);
container.bind<Database>((c) => MockDatabase());
service = container.make<UserService>();
});
test('creates user', () async {
var user = await service.create({
'name': 'John Doe',
'email': 'john@example.com'
});
expect(user.name, equals('John Doe'));
expect(user.email, equals('john@example.com'));
});
test('validates user data', () {
expect(
() => service.create({'name': 'John Doe'}),
throwsA(isA<ValidationException>())
);
});
});
}
```
### 2. Integration Tests
```dart
void main() {
group('API Integration', () {
late Application app;
setUp(() async {
app = await createApplication();
await app.initialize();
});
tearDown(() async {
await app.shutdown();
});
test('creates user through API', () async {
var response = await app.post('/users', body: {
'name': 'John Doe',
'email': 'john@example.com'
});
expect(response.statusCode, equals(201));
expect(response.json['name'], equals('John Doe'));
});
test('handles validation errors', () async {
var response = await app.post('/users', body: {
'name': 'John Doe'
});
expect(response.statusCode, equals(422));
expect(response.json['errors'], contains('email'));
});
});
}
```
### 3. Performance Tests
```dart
void main() {
group('Performance Tests', () {
late Application app;
setUp(() async {
app = await createApplication();
await app.initialize();
});
test('handles concurrent requests', () async {
var stopwatch = Stopwatch()..start();
// Create 100 concurrent requests
var futures = List.generate(100, (i) =>
app.get('/users')
);
var responses = await Future.wait(futures);
stopwatch.stop();
// Verify responses
expect(responses, everyElement(
predicate((r) => r.statusCode == 200)
));
// Check performance
expect(
stopwatch.elapsedMilliseconds / responses.length,
lessThan(100) // Less than 100ms per request
);
});
test('handles database operations efficiently', () async {
var stopwatch = Stopwatch()..start();
// Create 1000 records
for (var i = 0; i < 1000; i++) {
await app.post('/users', body: {
'name': 'User $i',
'email': 'user$i@example.com'
});
}
stopwatch.stop();
// Check performance
expect(
stopwatch.elapsedMilliseconds / 1000,
lessThan(50) // Less than 50ms per operation
);
});
});
}
```
### 4. Laravel-Style Feature Tests
```dart
void main() {
group('Feature Tests', () {
late TestCase test;
setUp(() {
test = await TestCase.make();
});
test('user can register', () async {
await test
.post('/register', {
'name': 'John Doe',
'email': 'john@example.com',
'password': 'password',
'password_confirmation': 'password'
})
.assertStatus(302)
.assertRedirect('/home');
test.assertDatabaseHas('users', {
'email': 'john@example.com'
});
});
test('user can login', () async {
// Create user
await test.createUser({
'email': 'john@example.com',
'password': 'password'
});
await test
.post('/login', {
'email': 'john@example.com',
'password': 'password'
})
.assertAuthenticated();
});
});
}
```
## Performance Testing Tools
### 1. WRK Benchmarking
```bash
# Basic load test
wrk -t12 -c400 -d30s http://localhost:8080/api/endpoint
# Test with custom script
wrk -t12 -c400 -d30s -s script.lua http://localhost:8080/api/endpoint
```
### 2. Custom Load Testing
```dart
void main() {
test('load test', () async {
var client = HttpClient();
var stopwatch = Stopwatch()..start();
// Configure test
var duration = Duration(minutes: 1);
var concurrency = 100;
var results = <Duration>[];
// Run test
while (stopwatch.elapsed < duration) {
var requests = List.generate(concurrency, (i) async {
var requestWatch = Stopwatch()..start();
await client.get('localhost', 8080, '/api/endpoint');
requestWatch.stop();
results.add(requestWatch.elapsed);
});
await Future.wait(requests);
}
// Analyze results
var average = results.reduce((a, b) => a + b) ~/ results.length;
var sorted = List.of(results)..sort();
var p95 = sorted[(sorted.length * 0.95).floor()];
var p99 = sorted[(sorted.length * 0.99).floor()];
print('Results:');
print('Average: ${average.inMilliseconds}ms');
print('P95: ${p95.inMilliseconds}ms');
print('P99: ${p99.inMilliseconds}ms');
});
}
```
## Best Practices
### 1. Test Organization
```dart
// Group related tests
group('UserService', () {
group('creation', () {
test('creates valid user', () {});
test('validates input', () {});
test('handles duplicates', () {});
});
group('authentication', () {
test('authenticates valid credentials', () {});
test('rejects invalid credentials', () {});
});
});
```
### 2. Test Data Management
```dart
class TestCase {
// Create test data
Future<User> createUser([Map<String, dynamic>? attributes]) async {
return factory.create(User, attributes);
}
// Clean up after tests
Future<void> cleanup() async {
await database.truncate(['users', 'posts', 'comments']);
}
}
```
### 3. Assertions
```dart
// Use descriptive assertions
expect(user.name, equals('John Doe'),
reason: 'User name should match input');
expect(response.statusCode,
isIn([200, 201]),
reason: 'Response should indicate success'
);
expect(
() => service.validateEmail('invalid'),
throwsA(isA<ValidationException>()),
reason: 'Should reject invalid email'
);
```
## Performance Benchmarks
### 1. Response Time Targets
```yaml
API Endpoints:
- Average: < 100ms
- P95: < 200ms
- P99: < 500ms
Database Operations:
- Simple queries: < 10ms
- Complex queries: < 50ms
- Writes: < 20ms
Cache Operations:
- Reads: < 5ms
- Writes: < 10ms
```
### 2. Throughput Targets
```yaml
API Layer:
- Minimum: 1000 requests/second
- Target: 5000 requests/second
Database Layer:
- Reads: 10000 operations/second
- Writes: 1000 operations/second
Cache Layer:
- Operations: 50000/second
```
### 3. Resource Usage Targets
```yaml
Memory:
- Base: < 100MB
- Under load: < 500MB
- Leak rate: < 1MB/hour
CPU:
- Idle: < 5%
- Average load: < 40%
- Peak load: < 80%
Connections:
- Database: < 100 concurrent
- Cache: < 1000 concurrent
- HTTP: < 10000 concurrent
```
## Next Steps
1. Implement test helpers
2. Add more Laravel-style assertions
3. Create performance test suite
4. Add continuous benchmarking
5. Improve test coverage
Would you like me to:
1. Create more test examples?
2. Add specific performance tests?
3. Create Laravel-compatible test helpers?

View file

@ -0,0 +1,466 @@
# Testing Package Specification
## Overview
The Testing package provides a robust testing framework that matches Laravel's testing functionality. It supports test case base classes, assertions, database testing, HTTP testing, and mocking while integrating with our Container and Event packages.
> **Related Documentation**
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for implementation status
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
> - See [Testing Guide](testing_guide.md) for testing approaches
> - See [Getting Started Guide](getting_started.md) for development setup
> - See [Container Package Specification](container_package_specification.md) for dependency injection
> - See [Events Package Specification](events_package_specification.md) for event testing
## Core Features
### 1. Test Case
```dart
/// Base test case class
abstract class TestCase {
/// Container instance
late Container container;
/// Application instance
late Application app;
/// Event dispatcher
late EventDispatcherContract events;
/// Sets up test case
@override
void setUp() {
container = Container();
app = Application(container);
events = container.make<EventDispatcherContract>();
setUpApplication();
registerServices();
}
/// Sets up application
void setUpApplication() {
app.singleton<Application>((c) => app);
app.singleton<Container>((c) => container);
app.singleton<EventDispatcherContract>((c) => events);
}
/// Registers test services
void registerServices() {}
/// Creates test instance
T make<T>([dynamic parameters]) {
return container.make<T>(parameters);
}
/// Runs test in transaction
Future<T> transaction<T>(Future<T> Function() callback) async {
var db = container.make<DatabaseManager>();
return await db.transaction(callback);
}
/// Refreshes database
Future<void> refreshDatabase() async {
await artisan.call('migrate:fresh');
}
/// Seeds database
Future<void> seed([String? class]) async {
await artisan.call('db:seed', [
if (class != null) '--class=$class'
]);
}
}
```
### 2. HTTP Testing
```dart
/// HTTP test case
abstract class HttpTestCase extends TestCase {
/// HTTP client
late TestClient client;
@override
void setUp() {
super.setUp();
client = TestClient(app);
}
/// Makes GET request
Future<TestResponse> get(String uri, {
Map<String, String>? headers,
Map<String, dynamic>? query
}) {
return client.get(uri, headers: headers, query: query);
}
/// Makes POST request
Future<TestResponse> post(String uri, {
Map<String, String>? headers,
dynamic body
}) {
return client.post(uri, headers: headers, body: body);
}
/// Makes PUT request
Future<TestResponse> put(String uri, {
Map<String, String>? headers,
dynamic body
}) {
return client.put(uri, headers: headers, body: body);
}
/// Makes DELETE request
Future<TestResponse> delete(String uri, {
Map<String, String>? headers
}) {
return client.delete(uri, headers: headers);
}
/// Acts as user
Future<void> actingAs(User user) async {
await auth.login(user);
}
}
/// Test HTTP client
class TestClient {
/// Application instance
final Application app;
TestClient(this.app);
/// Makes HTTP request
Future<TestResponse> request(
String method,
String uri, {
Map<String, String>? headers,
dynamic body,
Map<String, dynamic>? query
}) async {
var request = Request(method, uri)
..headers.addAll(headers ?? {})
..body = body
..uri = uri.replace(queryParameters: query);
var response = await app.handle(request);
return TestResponse(response);
}
}
/// Test HTTP response
class TestResponse {
/// Response instance
final Response response;
TestResponse(this.response);
/// Asserts response status
void assertStatus(int status) {
expect(response.statusCode, equals(status));
}
/// Asserts response is OK
void assertOk() {
assertStatus(200);
}
/// Asserts response is redirect
void assertRedirect([String? location]) {
expect(response.statusCode, inInclusiveRange(300, 399));
if (location != null) {
expect(response.headers['location'], equals(location));
}
}
/// Asserts response contains JSON
void assertJson(Map<String, dynamic> json) {
expect(response.json(), equals(json));
}
/// Asserts response contains text
void assertSee(String text) {
expect(response.body, contains(text));
}
}
```
### 3. Database Testing
```dart
/// Database test case
abstract class DatabaseTestCase extends TestCase {
/// Database manager
late DatabaseManager db;
@override
void setUp() {
super.setUp();
db = container.make<DatabaseManager>();
}
/// Seeds database
Future<void> seed(String seeder) async {
await artisan.call('db:seed', ['--class=$seeder']);
}
/// Asserts database has record
Future<void> assertDatabaseHas(
String table,
Map<String, dynamic> data
) async {
var count = await db.table(table)
.where(data)
.count();
expect(count, greaterThan(0));
}
/// Asserts database missing record
Future<void> assertDatabaseMissing(
String table,
Map<String, dynamic> data
) async {
var count = await db.table(table)
.where(data)
.count();
expect(count, equals(0));
}
/// Asserts database count
Future<void> assertDatabaseCount(
String table,
int count
) async {
var actual = await db.table(table).count();
expect(actual, equals(count));
}
}
```
### 4. Event Testing
```dart
/// Event test case
abstract class EventTestCase extends TestCase {
/// Fake event dispatcher
late FakeEventDispatcher events;
@override
void setUp() {
super.setUp();
events = FakeEventDispatcher();
container.instance<EventDispatcherContract>(events);
}
/// Asserts event dispatched
void assertDispatched(Type event, [Function? callback]) {
expect(events.dispatched(event), isTrue);
if (callback != null) {
var dispatched = events.dispatched(event, callback);
expect(dispatched, isTrue);
}
}
/// Asserts event not dispatched
void assertNotDispatched(Type event) {
expect(events.dispatched(event), isFalse);
}
/// Asserts nothing dispatched
void assertNothingDispatched() {
expect(events.hasDispatched(), isFalse);
}
}
/// Fake event dispatcher
class FakeEventDispatcher implements EventDispatcherContract {
/// Dispatched events
final List<dynamic> _events = [];
@override
Future<void> dispatch<T>(T event) async {
_events.add(event);
}
/// Checks if event dispatched
bool dispatched(Type event, [Function? callback]) {
var dispatched = _events.whereType<Type>();
if (dispatched.isEmpty) return false;
if (callback == null) return true;
return dispatched.any((e) => callback(e));
}
/// Checks if any events dispatched
bool hasDispatched() => _events.isNotEmpty;
}
```
## Integration Examples
### 1. HTTP Testing
```dart
class UserTest extends HttpTestCase {
test('creates user', () async {
var response = await post('/users', body: {
'name': 'John Doe',
'email': 'john@example.com'
});
response.assertStatus(201);
await assertDatabaseHas('users', {
'email': 'john@example.com'
});
});
test('requires authentication', () async {
var user = await User.factory().create();
await actingAs(user);
var response = await get('/dashboard');
response.assertOk();
});
}
```
### 2. Database Testing
```dart
class OrderTest extends DatabaseTestCase {
test('creates order', () async {
await seed(ProductSeeder);
var order = await Order.create({
'product_id': 1,
'quantity': 5
});
await assertDatabaseHas('orders', {
'id': order.id,
'quantity': 5
});
});
}
```
### 3. Event Testing
```dart
class PaymentTest extends EventTestCase {
test('dispatches payment events', () async {
var payment = await processPayment(order);
assertDispatched(PaymentProcessed, (event) {
return event.payment.id == payment.id;
});
});
}
```
## Testing
```dart
void main() {
group('HTTP Testing', () {
test('makes requests', () async {
var client = TestClient(app);
var response = await client.get('/users');
expect(response.statusCode, equals(200));
expect(response.json(), isA<List>());
});
test('handles authentication', () async {
var case = UserTest();
await case.setUp();
await case.actingAs(user);
var response = await case.get('/profile');
response.assertOk();
});
});
group('Database Testing', () {
test('seeds database', () async {
var case = OrderTest();
await case.setUp();
await case.seed(ProductSeeder);
await case.assertDatabaseCount('products', 10);
});
});
}
```
## Next Steps
1. Implement core testing features
2. Add HTTP testing
3. Add database testing
4. Add event testing
5. Write tests
6. Add examples
## Development Guidelines
### 1. Getting Started
Before implementing testing features:
1. Review [Getting Started Guide](getting_started.md)
2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Follow [Testing Guide](testing_guide.md)
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
5. Review [Container Package Specification](container_package_specification.md)
6. Review [Events Package Specification](events_package_specification.md)
### 2. Implementation Process
For each testing feature:
1. Write tests following [Testing Guide](testing_guide.md)
2. Implement following Laravel patterns
3. Document following [Getting Started Guide](getting_started.md#documentation)
4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md)
### 3. Quality Requirements
All implementations must:
1. Pass all tests (see [Testing Guide](testing_guide.md))
2. Meet Laravel compatibility requirements
3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md))
4. Support dependency injection (see [Container Package Specification](container_package_specification.md))
5. Support event testing (see [Events Package Specification](events_package_specification.md))
### 4. Integration Considerations
When implementing testing features:
1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md)
2. Ensure Laravel compatibility per [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
3. Use testing approaches from [Testing Guide](testing_guide.md)
4. Follow development setup in [Getting Started Guide](getting_started.md)
### 5. Performance Guidelines
Testing system must:
1. Execute tests efficiently
2. Support parallel testing
3. Handle large test suites
4. Manage test isolation
5. Meet performance targets in [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#performance-benchmarks)
### 6. Testing Requirements
Testing package tests must:
1. Cover all testing features
2. Test HTTP assertions
3. Verify database testing
4. Check event assertions
5. Follow patterns in [Testing Guide](testing_guide.md)
### 7. Documentation Requirements
Testing documentation must:
1. Explain testing patterns
2. Show assertion examples
3. Cover test organization
4. Include performance tips
5. Follow standards in [Getting Started Guide](getting_started.md#documentation)

View file

@ -13,6 +13,7 @@ repository: https://github.com/protevus/platform
packages: packages:
- apps/** - apps/**
- packages/** - packages/**
- sandbox/**
- helpers/tools/** - helpers/tools/**
- examples/** - examples/**

7
packages/bus/.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/
# Avoid committing pubspec.lock for library packages; see
# https://dart.dev/guides/libraries/private-files#pubspeclock.
pubspec.lock

View file

@ -0,0 +1,3 @@
## 1.0.0
- Initial version.

10
packages/bus/LICENSE.md Normal file
View file

@ -0,0 +1,10 @@
The MIT License (MIT)
The Laravel Framework is Copyright (c) Taylor Otwell
The Fabric Framework is Copyright (c) Vieo, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

1
packages/bus/README.md Normal file
View file

@ -0,0 +1 @@
<p align="center"><a href="https://protevus.com" target="_blank"><img src="https://git.protevus.com/protevus/branding/raw/branch/main/protevus-logo-bg.png"></a></p>

View file

@ -0,0 +1,30 @@
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.
include: package:lints/recommended.yaml
# Uncomment the following section to specify additional rules.
# linter:
# rules:
# - camel_case_types
# analyzer:
# exclude:
# - path/to/excluded/files/**
# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints
# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options

View file

@ -0,0 +1,9 @@
library angel3_bus;
export 'src/dispatcher.dart';
export 'src/command.dart';
export 'src/handler.dart';
export 'src/queue.dart';
export 'src/batch.dart';
export 'src/chain.dart';
export 'src/bus_service_provider.dart';

View file

@ -0,0 +1,19 @@
import 'command.dart';
import 'dispatcher.dart';
class Batch {
// Implement Batch
}
class PendingBatch {
final Dispatcher _dispatcher;
final List<Command> _commands;
PendingBatch(this._dispatcher, this._commands);
Future<void> dispatch() async {
for (var command in _commands) {
await _dispatcher.dispatch(command);
}
}
}

View file

@ -0,0 +1,60 @@
// // lib/src/bus_service_provider.dart
// import 'package:angel3_framework/angel3_framework.dart';
// import 'package:angel3_event_bus/angel3_event_bus.dart';
// import 'package:angel3_mq/angel3_mq.dart';
// import 'dispatcher.dart';
// class BusServiceProvider extends Provider {
// @override
// Future<void> boot(Angel app) async {
// // Register EventBus
// app.container.registerSingleton<EventBus>(EventBus());
// // Register Queue
// app.container.registerSingleton<Queue>(MemoryQueue());
// // Create and register the Dispatcher
// final dispatcher = Dispatcher(app.container);
// app.container.registerSingleton<Dispatcher>(dispatcher);
// // Register any global middleware or mappings
// dispatcher.pipeThrough([
// // Add any global middleware here
// ]);
// // Register command-to-handler mappings
// dispatcher.map({
// // Add your command-to-handler mappings here
// // Example: ExampleCommand: ExampleCommandHandler,
// });
// }
// }
// class MemoryQueue implements Queue {
// final List<Command> _queue = [];
// @override
// Future<void> push(Command command) async {
// _queue.add(command);
// }
// @override
// Future<void> later(Duration delay, Command command) async {
// await Future.delayed(delay);
// _queue.add(command);
// }
// @override
// Future<void> pushOn(String queue, Command command) async {
// // For simplicity, ignoring the queue parameter in this implementation
// _queue.add(command);
// }
// @override
// Future<void> laterOn(String queue, Duration delay, Command command) async {
// // For simplicity, ignoring the queue parameter in this implementation
// await Future.delayed(delay);
// _queue.add(command);
// }
// }

View file

@ -0,0 +1,15 @@
import 'command.dart';
import 'dispatcher.dart';
class PendingChain {
final Dispatcher _dispatcher;
final List<Command> _commands;
PendingChain(this._dispatcher, this._commands);
Future<void> dispatch() async {
for (var command in _commands) {
await _dispatcher.dispatch(command);
}
}
}

View file

@ -0,0 +1,5 @@
// lib/src/command.dart
abstract class Command {}
abstract class ShouldQueue implements Command {}

View file

@ -0,0 +1,251 @@
// lib/src/dispatcher.dart
import 'dart:async';
import 'package:platform_container/container.dart';
import 'package:angel3_reactivex/angel3_reactivex.dart';
import 'package:angel3_event_bus/event_bus.dart';
import 'package:angel3_mq/mq.dart';
import 'command.dart';
import 'handler.dart';
import 'batch.dart';
import 'chain.dart';
/// A class that handles dispatching and processing of commands.
///
/// This dispatcher supports both synchronous and asynchronous command execution,
/// as well as queueing commands for later processing.
class Dispatcher implements QueueingDispatcher {
final Container container;
final EventBus _eventBus;
final Subject<Command> _commandSubject;
final MQClient _queue;
final Map<Type, Type> _handlers = {};
/// Creates a new [Dispatcher] instance.
///
/// [container] is used for dependency injection and to retrieve necessary services.
Dispatcher(this.container)
: _eventBus = container.make<EventBus>(),
_commandSubject = BehaviorSubject<Command>(),
_queue = container.make<MQClient>() {
_setupCommandProcessing();
}
/// Sets up the command processing pipeline.
///
/// This method initializes the stream that processes commands and emits events.
void _setupCommandProcessing() {
_commandSubject
.flatMap((command) => Stream.fromFuture(_processCommand(command))
.map((result) => CommandEvent(command, result: result))
.onErrorReturnWith(
(error, stackTrace) => CommandEvent(command, error: error)))
.listen((event) {
_eventBus.fire(event);
});
}
/// Dispatches a command for execution.
///
/// If the command implements [ShouldQueue], it will be dispatched to a queue.
/// Otherwise, it will be executed immediately.
///
/// [command] is the command to be dispatched.
@override
Future<dynamic> dispatch(Command command) {
if (command is ShouldQueue) {
return dispatchToQueue(command);
} else {
return dispatchNow(command);
}
}
/// Dispatches a command for immediate execution.
///
/// [command] is the command to be executed.
/// [handler] is an optional specific handler for the command.
@override
Future<dynamic> dispatchNow(Command command, [Handler? handler]) {
final completer = Completer<dynamic>();
_commandSubject.add(command);
_eventBus
.on<CommandEvent>()
.where((event) => event.command == command)
.take(1)
.listen((event) {
if (event.error != null) {
completer.completeError(event.error);
} else {
completer.complete(event.result);
}
});
return completer.future;
}
/// Processes a command by finding and executing its appropriate handler.
///
/// [command] is the command to be processed.
Future<dynamic> _processCommand(Command command) async {
final handlerType = _handlers[command.runtimeType];
if (handlerType != null) {
final handler = container.make(handlerType) as Handler;
return await handler.handle(command);
} else {
throw Exception('No handler found for command: ${command.runtimeType}');
}
}
/// Dispatches a command to a queue for later processing.
///
/// [command] is the command to be queued.
@override
Future<dynamic> dispatchToQueue(Command command) async {
final message = Message(
payload: command,
headers: {
'commandType': command.runtimeType.toString(),
},
);
_queue.sendMessage(
message: message,
// You might want to specify an exchange name and routing key if needed
// exchangeName: 'your_exchange_name',
// routingKey: 'your_routing_key',
);
return message.id;
}
/// Dispatches a command synchronously.
///
/// This is an alias for [dispatchNow].
///
/// [command] is the command to be executed.
/// [handler] is an optional specific handler for the command.
@override
Future<dynamic> dispatchSync(Command command, [Handler? handler]) {
return dispatchNow(command, handler);
}
/// Finds a batch by its ID.
///
/// [batchId] is the ID of the batch to find.
@override
Future<Batch?> findBatch(String batchId) async {
// Implement batch finding logic
throw UnimplementedError();
}
/// Creates a new pending batch of commands.
///
/// [commands] is the list of commands to be included in the batch.
@override
PendingBatch batch(List<Command> commands) {
return PendingBatch(this, commands);
}
/// Creates a new pending chain of commands.
///
/// [commands] is the list of commands to be included in the chain.
@override
PendingChain chain(List<Command> commands) {
return PendingChain(this, commands);
}
/// Applies a list of pipes to the command processing pipeline.
///
/// [pipes] is the list of pipes to be applied.
@override
Dispatcher pipeThrough(List<Pipe> pipes) {
_commandSubject.transform(
StreamTransformer.fromHandlers(
handleData: (data, sink) {
var result = data;
for (var pipe in pipes) {
result = pipe(result);
}
sink.add(result);
},
),
);
return this;
}
/// Maps command types to their respective handler types.
///
/// [handlers] is a map where keys are command types and values are handler types.
@override
Dispatcher map(Map<Type, Type> handlers) {
_handlers.addAll(handlers);
return this;
}
/// Dispatches a command to be executed after the current request-response cycle.
///
/// [command] is the command to be dispatched after the response.
@override
void dispatchAfterResponse(Command command) {
final message = Message(
payload: command,
headers: {
'commandType': command.runtimeType.toString(),
'dispatchAfterResponse': 'true',
},
);
_queue.sendMessage(
message: message,
// You might want to specify an exchange name if needed
// exchangeName: 'your_exchange_name',
// If you want to use a specific queue for after-response commands:
routingKey: 'after_response_queue',
);
}
}
abstract class QueueingDispatcher {
Future<dynamic> dispatch(Command command);
Future<dynamic> dispatchSync(Command command, [Handler? handler]);
Future<dynamic> dispatchNow(Command command, [Handler? handler]);
Future<dynamic> dispatchToQueue(Command command);
Future<Batch?> findBatch(String batchId);
PendingBatch batch(List<Command> commands);
PendingChain chain(List<Command> commands);
Dispatcher pipeThrough(List<Pipe> pipes);
Dispatcher map(Map<Type, Type> handlers);
void dispatchAfterResponse(Command command);
}
typedef Pipe = Command Function(Command);
class CommandCompletedEvent extends AppEvent {
final dynamic result;
CommandCompletedEvent(this.result);
@override
List<Object?> get props => [result];
}
class CommandErrorEvent extends AppEvent {
final dynamic error;
CommandErrorEvent(this.error);
@override
List<Object?> get props => [error];
}
class CommandEvent extends AppEvent {
final Command command;
final dynamic result;
final dynamic error;
CommandEvent(this.command, {this.result, this.error});
@override
List<Object?> get props => [command, result, error];
}

View file

@ -0,0 +1,5 @@
import 'command.dart';
abstract class Handler {
Future<dynamic> handle(Command command);
}

View file

@ -0,0 +1,8 @@
import 'command.dart';
abstract class Queue {
Future<void> push(Command command);
Future<void> later(Duration delay, Command command);
Future<void> pushOn(String queue, Command command);
Future<void> laterOn(String queue, Duration delay, Command command);
}

24
packages/bus/pubspec.yaml Normal file
View file

@ -0,0 +1,24 @@
name: platform_bus
description: The Bus Package for the Protevus Platform
version: 0.0.1
homepage: https://protevus.com
documentation: https://docs.protevus.com
repository: https://github.com/protevus/platformo
environment:
sdk: ^3.4.2
# Add regular dependencies here.
dependencies:
platform_container: ^9.0.0
platform_core: ^9.0.0
angel3_reactivex: ^9.0.0
angel3_event_bus: ^9.0.0
angel3_mq: ^9.0.0
# path: ^1.8.0
dev_dependencies:
build_runner: ^2.1.0
lints: ^3.0.0
mockito: ^5.3.0
test: ^1.24.0

View file

@ -0,0 +1,197 @@
import 'dart:async';
import 'package:platform_bus/angel3_bus.dart';
import 'package:platform_container/container.dart';
import 'package:angel3_event_bus/event_bus.dart';
import 'package:angel3_mq/mq.dart';
import 'package:mockito/mockito.dart';
import 'package:test/test.dart';
class IsMessage extends Matcher {
@override
bool matches(item, Map matchState) => item is Message;
@override
Description describe(Description description) =>
description.add('is a Message');
}
class MockContainer extends Mock implements Container {
final Map<Type, dynamic> _instances = {};
@override
T make<T>([Type? type]) {
type ??= T;
return _instances[type] as T;
}
void registerInstance<T>(T instance) {
_instances[T] = instance;
}
}
class MockEventBus extends Mock implements EventBus {
@override
Stream<T> on<T extends AppEvent>() {
return super.noSuchMethod(
Invocation.method(#on, [], {#T: T}),
returnValue: Stream<T>.empty(),
) as Stream<T>;
}
}
class MockMQClient extends Mock implements MQClient {
Message? capturedMessage;
String? capturedExchangeName;
String? capturedRoutingKey;
@override
dynamic noSuchMethod(Invocation invocation,
{Object? returnValue, Object? returnValueForMissingStub}) {
if (invocation.memberName == #sendMessage) {
final namedArgs = invocation.namedArguments;
capturedMessage = namedArgs[#message] as Message?;
capturedExchangeName = namedArgs[#exchangeName] as String?;
capturedRoutingKey = namedArgs[#routingKey] as String?;
return null;
}
return super.noSuchMethod(invocation,
returnValue: returnValue,
returnValueForMissingStub: returnValueForMissingStub);
}
}
class TestCommand implements Command {
final String data;
TestCommand(this.data);
}
class TestHandler implements Handler {
@override
Future<dynamic> handle(Command command) async {
if (command is TestCommand) {
return 'Handled: ${command.data}';
}
throw UnimplementedError();
}
}
class TestQueuedCommand implements Command, ShouldQueue {
final String data;
TestQueuedCommand(this.data);
}
void main() {
late MockContainer container;
late MockEventBus eventBus;
late MockMQClient mqClient;
late Dispatcher dispatcher;
setUp(() {
container = MockContainer();
eventBus = MockEventBus();
mqClient = MockMQClient();
container.registerInstance<EventBus>(eventBus);
container.registerInstance<MQClient>(mqClient);
dispatcher = Dispatcher(container);
});
group('Dispatcher', () {
test('dispatchNow should handle command and return result', () async {
final command = TestCommand('test data');
final handler = TestHandler();
container.registerInstance<TestHandler>(handler);
dispatcher.map({TestCommand: TestHandler});
final commandEventController = StreamController<CommandEvent>();
when(eventBus.on<CommandEvent>())
.thenAnswer((_) => commandEventController.stream);
final future = dispatcher.dispatchNow(command);
// Simulate the event firing
commandEventController
.add(CommandEvent(command, result: 'Handled: test data'));
final result = await future;
expect(result, equals('Handled: test data'));
await commandEventController.close();
});
test('dispatch should handle regular commands immediately', () async {
final command = TestCommand('regular');
final handler = TestHandler();
container.registerInstance<TestHandler>(handler);
dispatcher.map({TestCommand: TestHandler});
final commandEventController = StreamController<CommandEvent>();
when(eventBus.on<CommandEvent>())
.thenAnswer((_) => commandEventController.stream);
final future = dispatcher.dispatch(command);
// Simulate the event firing
commandEventController
.add(CommandEvent(command, result: 'Handled: regular'));
final result = await future;
expect(result, equals('Handled: regular'));
await commandEventController.close();
});
test('dispatch should queue ShouldQueue commands', () async {
final command = TestQueuedCommand('queued data');
// Dispatch the command
await dispatcher.dispatch(command);
// Verify that sendMessage was called and check the message properties
expect(mqClient.capturedMessage, isNotNull);
expect(mqClient.capturedMessage!.payload, equals(command));
expect(mqClient.capturedMessage!.headers?['commandType'],
equals('TestQueuedCommand'));
// Optionally, verify exchange name and routing key if needed
expect(mqClient.capturedExchangeName, isNull);
expect(mqClient.capturedRoutingKey, isNull);
});
test(
'dispatchAfterResponse should send message to queue with specific header',
() {
final command = TestCommand('after response data');
// Call dispatchAfterResponse
dispatcher.dispatchAfterResponse(command);
// Verify that sendMessage was called and check the message properties
expect(mqClient.capturedMessage, isNotNull);
expect(mqClient.capturedMessage!.payload, equals(command));
expect(mqClient.capturedMessage!.headers?['commandType'],
equals('TestCommand'));
expect(mqClient.capturedMessage!.headers?['dispatchAfterResponse'],
equals('true'));
// Verify routing key
expect(mqClient.capturedRoutingKey, equals('after_response_queue'));
// Optionally, verify exchange name if needed
expect(mqClient.capturedExchangeName, isNull);
});
test('map should register command handlers', () {
dispatcher.map({TestCommand: TestHandler});
// Mock the event bus behavior for this test
when(eventBus.on<CommandEvent>()).thenAnswer((_) => Stream.empty());
// This test is a bit tricky to verify directly, but we can check if dispatch doesn't throw
expect(() => dispatcher.dispatch(TestCommand('test')), returnsNormally);
});
});
}

7
packages/events/.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/
# Avoid committing pubspec.lock for library packages; see
# https://dart.dev/guides/libraries/private-files#pubspeclock.
pubspec.lock

View file

@ -0,0 +1,3 @@
## 1.0.0
- Initial version.

View file

@ -0,0 +1,10 @@
The MIT License (MIT)
The Laravel Framework is Copyright (c) Taylor Otwell
The Fabric Framework is Copyright (c) Vieo, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -0,0 +1 @@
<p align="center"><a href="https://protevus.com" target="_blank"><img src="https://git.protevus.com/protevus/branding/raw/branch/main/protevus-logo-bg.png"></a></p>

View file

@ -0,0 +1,30 @@
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.
include: package:lints/recommended.yaml
# Uncomment the following section to specify additional rules.
# linter:
# rules:
# - camel_case_types
# analyzer:
# exclude:
# - path/to/excluded/files/**
# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints
# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options

View file

@ -0,0 +1,3 @@
library;
export 'src/dispatcher.dart';

View file

@ -0,0 +1,499 @@
import 'dart:async';
import 'package:platform_container/container.dart';
import 'package:angel3_reactivex/angel3_reactivex.dart';
import 'package:angel3_event_bus/event_bus.dart';
import 'package:angel3_mq/mq.dart';
// Simulating some of the Laravel interfaces/classes
abstract class ShouldBroadcast {}
abstract class ShouldQueue {}
abstract class ShouldBeEncrypted {}
abstract class ShouldDispatchAfterCommit {}
class Dispatcher implements DispatcherContract {
// Properties as specified in YAML
final Container container;
final Map<String, List<Function>> _listeners = {};
final Map<String, List<Function>> _wildcards = {};
final Map<String, List<Function>> _wildcardsCache = {};
late final Function _queueResolver;
late final Function _transactionManagerResolver;
final Map<String, List<Function>> _eventBusListeners = {};
final Map<String, Completer<dynamic>> _untilCompleters = {};
final Map<String, StreamSubscription> _eventBusSubscriptions = {};
final Set<String> _processedMessageIds = {};
// Properties for Angel3 packages
final EventBus _eventBus;
late final MQClient? _mqClient;
final Map<String, BehaviorSubject<dynamic>> _subjects = {};
// Queue and exchange names
static const String _eventsQueue = 'events_queue';
static const String _delayedEventsQueue = 'delayed_events_queue';
static const String _eventsExchange = 'events_exchange';
Dispatcher(this.container) : _eventBus = EventBus();
// Setter for _mqClient
set mqClient(MQClient client) {
_mqClient = client;
_setupQueuesAndExchanges();
_startProcessingQueuedEvents();
}
void _setupQueuesAndExchanges() {
_mqClient?.declareQueue(_eventsQueue);
_mqClient?.declareQueue(_delayedEventsQueue);
_mqClient?.declareExchange(
exchangeName: _eventsExchange,
exchangeType: ExchangeType.direct,
);
_mqClient?.bindQueue(
queueId: _eventsQueue,
exchangeName: _eventsExchange,
bindingKey: _eventsQueue,
);
_mqClient?.bindQueue(
queueId: _delayedEventsQueue,
exchangeName: _eventsExchange,
bindingKey: _delayedEventsQueue,
);
}
void _startProcessingQueuedEvents() {
_mqClient?.fetchQueue(_eventsQueue).listen((Message message) async {
if (message.payload is Map) {
final eventData = message.payload as Map<String, dynamic>;
if (eventData.containsKey('event') &&
eventData.containsKey('payload')) {
await dispatch(eventData['event'], eventData['payload']);
} else {
print('Invalid message format: ${message.payload}');
}
} else {
print('Unexpected payload type: ${message.payload.runtimeType}');
}
});
}
@override
void listen(dynamic events, dynamic listener) {
if (events is String) {
_addListener(events, listener);
} else if (events is List) {
for (var event in events) {
_addListener(event, listener);
}
}
if (events is String && events.contains('*')) {
_setupWildcardListen(events, listener);
}
}
void _addListener(String event, dynamic listener) {
_listeners.putIfAbsent(event, () => []).add(listener);
// Create a subject for this event if it doesn't exist
_subjects.putIfAbsent(event, () => BehaviorSubject<dynamic>());
// Add EventBus listener and store the subscription
final subscription = _eventBus.on().listen((AppEvent busEvent) {
if (busEvent is CustomAppEvent && busEvent.eventName == event) {
listener(event, busEvent.payload);
}
});
_eventBusSubscriptions[event] = subscription;
}
void _setupWildcardListen(String event, Function listener) {
_wildcards.putIfAbsent(event, () => []).add(listener);
_wildcardsCache.clear();
}
@override
bool hasListeners(String eventName) {
return _listeners.containsKey(eventName) ||
_wildcards.containsKey(eventName) ||
hasWildcardListeners(eventName);
}
bool hasWildcardListeners(String eventName) {
return _wildcards.keys
.any((pattern) => _isWildcardMatch(pattern, eventName));
}
@override
void push(String event, [dynamic payload]) {
final effectivePayload = payload ?? [];
_mqClient?.sendMessage(
exchangeName: _eventsExchange,
routingKey: _delayedEventsQueue,
message: Message(
headers: {'expiration': '5000'}, // 5 seconds delay
payload: {
'event': event,
'payload':
effectivePayload is List ? effectivePayload : [effectivePayload],
},
timestamp: DateTime.now().toIso8601String(),
id: 'msg_${DateTime.now().millisecondsSinceEpoch}', // Ensure unique ID
),
);
}
@override
Future<void> flush(String event) async {
final messageStream = _mqClient?.fetchQueue(_delayedEventsQueue);
if (messageStream == null) {
print('Warning: MQClient is not initialized');
return;
}
final messagesToProcess = <Message>[];
// Collect messages to process
await for (final message in messageStream) {
print('Examining message: ${message.id}');
if (message.payload is Map<String, dynamic> &&
!_processedMessageIds.contains(message.id)) {
final eventData = message.payload as Map<String, dynamic>;
if (eventData['event'] == event) {
print('Adding message to process: ${message.id}');
messagesToProcess.add(message);
}
}
}
print('Total messages to process: ${messagesToProcess.length}');
// Process collected messages
for (final message in messagesToProcess) {
final eventData = message.payload as Map<String, dynamic>;
print('Processing message: ${message.id}');
await dispatch(eventData['event'], eventData['payload']);
_mqClient?.deleteMessage(_delayedEventsQueue, message);
_processedMessageIds.add(message.id);
}
}
@override
void subscribe(dynamic subscriber) {
if (subscriber is EventBusSubscriber) {
subscriber.subscribe(_eventBus);
} else {
// Handle other types of subscribers
}
}
@override
Future<dynamic> until(dynamic event, [dynamic payload]) {
if (event is String) {
final completer = Completer<dynamic>();
_untilCompleters[event] = completer;
// Set up a one-time listener for this event
listen(event, (dynamic e, dynamic p) {
if (!completer.isCompleted) {
completer.complete(p);
_untilCompleters.remove(event);
}
});
// If payload is provided, dispatch the event immediately
if (payload != null) {
// Use dispatch instead of push to ensure immediate processing
dispatch(event, payload);
}
return completer.future;
}
throw ArgumentError('Event must be a String');
}
@override
Future<dynamic> dispatch(dynamic event, [dynamic payload, bool? halt]) async {
final eventName = event is String ? event : event.runtimeType.toString();
final eventPayload = payload ?? (event is AppEvent ? event : []);
if (event is ShouldBroadcast ||
(eventPayload is List &&
eventPayload.isNotEmpty &&
eventPayload[0] is ShouldBroadcast)) {
await _broadcastEvent(event);
}
if (event is ShouldQueue ||
(eventPayload is List &&
eventPayload.isNotEmpty &&
eventPayload[0] is ShouldQueue)) {
return _queueEvent(eventName, eventPayload);
}
final listeners = getListeners(eventName);
for (var listener in listeners) {
final response =
await Function.apply(listener, [eventName, eventPayload]);
if (halt == true && response != null) {
return response;
}
if (response == false) {
break;
}
}
return halt == true ? null : listeners;
}
// void _addToSubject(String eventName, dynamic payload) {
// if (_subjects.containsKey(eventName)) {
// _subjects[eventName]!.add(payload);
// }
// }
@override
List<Function> getListeners(String eventName) {
var listeners = <Function>[
...(_listeners[eventName] ?? []),
...(_wildcardsCache[eventName] ?? _getWildcardListeners(eventName)),
...(_eventBusListeners[eventName] ?? []),
];
return listeners;
}
List<Function> _getWildcardListeners(String eventName) {
final wildcardListeners = <Function>[];
for (var entry in _wildcards.entries) {
if (_isWildcardMatch(entry.key, eventName)) {
wildcardListeners.addAll(entry.value);
}
}
_wildcardsCache[eventName] = wildcardListeners;
return wildcardListeners;
}
@override
void forget(String event) {
// Remove from _listeners
_listeners.remove(event);
// Remove from _subjects
if (_subjects.containsKey(event)) {
_subjects[event]?.close();
_subjects.remove(event);
}
// Cancel and remove EventBus subscription
_eventBusSubscriptions[event]?.cancel();
_eventBusSubscriptions.remove(event);
// Remove from wildcards if applicable
if (event.contains('*')) {
_wildcards.remove(event);
_wildcardsCache.clear();
} else {
// If it's not a wildcard, we need to remove it from any matching wildcard listeners
_wildcards.forEach((pattern, listeners) {
if (_isWildcardMatch(pattern, event)) {
_wildcards[pattern] = listeners
.where((listener) => listener != _listeners[event])
.toList();
}
});
_wildcardsCache.clear();
}
// Remove any 'until' completers for this event
_untilCompleters.remove(event);
}
@override
void forgetPushed() {
_listeners.removeWhere((key, _) => key.endsWith('_pushed'));
_eventBusListeners.removeWhere((key, _) => key.endsWith('_pushed'));
// Note: We're not clearing all EventBus listeners here, as that might affect other parts of your application
}
@override
void setQueueResolver(Function resolver) {
_queueResolver = resolver;
}
@override
void setTransactionManagerResolver(Function resolver) {
_transactionManagerResolver = resolver;
}
// Add these methods for testing purposes
void triggerQueueResolver() {
_queueResolver();
}
void triggerTransactionManagerResolver() {
_transactionManagerResolver();
}
@override
Map<String, List<Function>> getRawListeners() {
return Map.unmodifiable(_listeners);
}
bool _shouldBroadcast(List payload) {
return payload.isNotEmpty && payload[0] is ShouldBroadcast;
}
Future<void> _broadcastEvent(dynamic event) async {
// Implement broadcasting logic here
// For now, we'll just print a message
print('Broadcasting event: ${event.runtimeType}');
}
bool _isWildcardMatch(String pattern, String eventName) {
final regExp = RegExp('^${pattern.replaceAll('*', '.*')}\$');
return regExp.hasMatch(eventName);
}
bool _shouldQueue(List payload) {
return payload.isNotEmpty && payload[0] is ShouldQueue;
}
Future<void> _queueEvent(String eventName, dynamic payload) async {
_mqClient?.sendMessage(
exchangeName: _eventsExchange,
routingKey: _eventsQueue,
message: Message(
payload: {'event': eventName, 'payload': payload},
timestamp: DateTime.now().toIso8601String(),
),
);
}
// Updated on<T> method
Stream<T> on<T>(String event) {
return (_subjects
.putIfAbsent(event, () => BehaviorSubject<dynamic>())
.stream as Stream<T>)
.where((event) => event is T)
.cast<T>();
}
// In your Dispatcher class
void setMQClient(MQClient client) {
_mqClient = client;
}
// Method to close the MQClient connection
Future<void> close() async {
_mqClient?.close();
}
// Don't forget to close the subjects when they're no longer needed
void dispose() {
for (var subject in _subjects.values) {
subject.close();
}
}
}
// ... rest of the code (DispatcherContract, EventBusSubscriber, etc.) remains the same
abstract class DispatcherContract {
void listen(dynamic events, dynamic listener);
bool hasListeners(String eventName);
void push(String event, [dynamic payload]);
Future<void> flush(String event);
void subscribe(dynamic subscriber);
Future<dynamic> until(dynamic event, [dynamic payload]);
Future<dynamic> dispatch(dynamic event, [dynamic payload, bool halt]);
List<Function> getListeners(String eventName);
void forget(String event);
void forgetPushed();
void setQueueResolver(Function resolver);
void setTransactionManagerResolver(Function resolver);
Map<String, List<Function>> getRawListeners();
}
// Helper class for EventBus subscribers
abstract class EventBusSubscriber {
void subscribe(EventBus eventBus);
}
// Mixin to simulate Macroable trait
mixin Macroable {
// Implementation of Macroable functionality
}
// Mixin to simulate ReflectsClosures trait
mixin ReflectsClosures {
// Implementation of ReflectsClosures functionality
}
// If not already defined, you might need to create an Event class
class Event {
final String name;
final dynamic data;
Event(this.name, this.data);
}
// Custom AppEvent subclasses for handling different event types
class StringBasedEvent extends AppEvent {
final String eventName;
final dynamic payload;
StringBasedEvent(this.eventName, this.payload);
@override
List<Object?> get props => [eventName, payload];
}
class CustomAppEvent extends AppEvent {
final String eventName;
final dynamic payload;
CustomAppEvent(this.eventName, this.payload);
@override
List<Object?> get props => [eventName, payload];
}
// This is a simple implementation of Reflector that does nothing
class EmptyReflector implements Reflector {
const EmptyReflector();
@override
ReflectedType reflectType(Type type) {
throw UnimplementedError();
}
@override
ReflectedInstance reflectInstance(Object object) {
throw UnimplementedError();
}
@override
ReflectedType reflectFutureOf(Type type) {
throw UnimplementedError();
}
@override
String? getName(Symbol symbol) {
// TODO: implement getName
throw UnimplementedError();
}
@override
ReflectedClass? reflectClass(Type clazz) {
// TODO: implement reflectClass
throw UnimplementedError();
}
@override
ReflectedFunction? reflectFunction(Function function) {
// TODO: implement reflectFunction
throw UnimplementedError();
}
}

View file

@ -0,0 +1,21 @@
name: platform_events
description: The Events Package for the Protevus Platform
version: 0.0.1
homepage: https://protevus.com
documentation: https://docs.protevus.com
repository: https://github.com/protevus/platformo
environment:
sdk: ^3.4.2
# Add regular dependencies here.
dependencies:
platform_container: ^9.0.0
angel3_mq: ^9.0.0
angel3_event_bus: ^9.0.0
platform_core: ^9.0.0
angel3_reactivex: ^0.27.5
# path: ^1.8.0
dev_dependencies:
lints: ^3.0.0
test: ^1.24.0

View file

@ -0,0 +1,430 @@
import 'package:angel3_event_bus/res/app_event.dart';
import 'package:test/test.dart';
import 'package:platform_container/container.dart';
import 'package:angel3_mq/mq.dart';
import 'package:platform_events/dispatcher.dart'; // Replace with the actual import path
void main() {
late Dispatcher dispatcher;
late MockMQClient mockMQClient;
setUp(() {
var container = Container(EmptyReflector());
dispatcher = Dispatcher(container);
mockMQClient = MockMQClient();
dispatcher.mqClient = mockMQClient; // Use the setter
// Clear the queue before each test
mockMQClient.queuedMessages.clear();
});
group('Dispatcher', () {
test('listen and dispatch', () async {
var callCount = 0;
dispatcher.listen('test_event', (dynamic event, dynamic payload) {
expect(event, equals('test_event'));
expect(payload, equals(['test_payload']));
callCount++;
});
await dispatcher.dispatch('test_event', ['test_payload']);
expect(callCount, equals(1));
});
test('wildcard listener', () async {
var callCount = 0;
dispatcher.listen('test.*', (dynamic event, dynamic payload) {
expect(event, matches(RegExp(r'^test\.')));
callCount++;
});
await dispatcher.dispatch('test.one', ['payload1']);
await dispatcher.dispatch('test.two', ['payload2']);
expect(callCount, equals(2));
});
test('hasListeners', () {
dispatcher.listen('test_event', (dynamic event, dynamic payload) {});
expect(dispatcher.hasListeners('test_event'), isTrue);
expect(dispatcher.hasListeners('non_existent_event'), isFalse);
});
test('until', () async {
// Test without pushing the event immediately
var futureResult = dispatcher.until('test_event');
// Use a small delay to ensure the until listener is set up
await Future.delayed(Duration(milliseconds: 10));
await dispatcher.dispatch('test_event', ['test_payload']);
var result = await futureResult;
expect(result, equals(['test_payload']));
// Test with pushing the event immediately
result =
await dispatcher.until('another_test_event', ['another_payload']);
expect(result, equals(['another_payload']));
}, timeout: Timeout(Duration(seconds: 5))); // Add a reasonable timeout
test('forget', () async {
var callCount = 0;
dispatcher.listen('test_event', (dynamic event, dynamic payload) {
callCount++;
});
await dispatcher.dispatch('test_event');
expect(callCount, equals(1));
dispatcher.forget('test_event');
await dispatcher.dispatch('test_event');
expect(callCount, equals(1)); // Should not increase
});
test('push and flush', () async {
print('Starting push and flush test');
// Push 4 messages
for (var i = 0; i < 4; i++) {
dispatcher.push('delayed_event', ['delayed_payload_$i']);
}
// Verify that 4 messages were queued
expect(mockMQClient.queuedMessages['delayed_events_queue']?.length,
equals(4),
reason: 'Should have queued exactly 4 messages');
print(
'Queued messages: ${mockMQClient.queuedMessages['delayed_events_queue']?.length}');
var callCount = 0;
var processedPayloads = <String>[];
// Remove any existing listeners
dispatcher.forget('delayed_event');
dispatcher.listen('delayed_event', (dynamic event, dynamic payload) {
print('Listener called with payload: $payload');
expect(event, equals('delayed_event'));
expect(payload[0], startsWith('delayed_payload_'));
processedPayloads.add(payload[0]);
callCount++;
});
await dispatcher.flush('delayed_event');
print('After flush - Call count: $callCount');
print('Processed payloads: $processedPayloads');
expect(callCount, equals(4), reason: 'Should process exactly 4 messages');
expect(processedPayloads.toSet().length, equals(4),
reason: 'All payloads should be unique');
// Verify that all messages were removed from the queue
expect(mockMQClient.queuedMessages['delayed_events_queue']?.length,
equals(0),
reason: 'Queue should be empty after flush');
// Flush again to ensure no more messages are processed
await dispatcher.flush('delayed_event');
expect(callCount, equals(4),
reason: 'Should still be 4 after second flush');
});
test('shouldBroadcast', () async {
var broadcastEvent = BroadcastTestEvent();
var callCount = 0;
dispatcher.listen('BroadcastTestEvent', (dynamic event, dynamic payload) {
callCount++;
});
await dispatcher.dispatch(broadcastEvent);
expect(callCount, equals(1));
});
test('shouldQueue', () async {
var queueEvent = QueueTestEvent();
await dispatcher.dispatch(queueEvent);
expect(mockMQClient.queuedMessages['events_queue'], isNotEmpty);
expect(mockMQClient.queuedMessages['events_queue']!.first.payload,
containsPair('event', 'QueueTestEvent'));
});
test('forgetPushed removes only pushed events', () {
dispatcher.listen('event_pushed', (_, __) {});
dispatcher.listen('normal_event', (_, __) {});
dispatcher.forgetPushed();
expect(dispatcher.hasListeners('event_pushed'), isFalse);
expect(dispatcher.hasListeners('normal_event'), isTrue);
});
test('setQueueResolver and setTransactionManagerResolver', () {
var queueResolverCalled = false;
var transactionManagerResolverCalled = false;
dispatcher.setQueueResolver(() {
queueResolverCalled = true;
});
dispatcher.setTransactionManagerResolver(() {
transactionManagerResolverCalled = true;
});
// Trigger the resolvers
dispatcher.triggerQueueResolver();
dispatcher.triggerTransactionManagerResolver();
expect(queueResolverCalled, isTrue);
expect(transactionManagerResolverCalled, isTrue);
});
test('getRawListeners returns unmodifiable map', () {
dispatcher.listen('test_event', (_, __) {});
var rawListeners = dispatcher.getRawListeners();
expect(rawListeners, isA<Map<String, List<Function>>>());
expect(() => rawListeners['new_event'] = [], throwsUnsupportedError);
});
test('multiple listeners for same event', () async {
var callCount1 = 0;
var callCount2 = 0;
dispatcher.listen('multi_event', (_, __) => callCount1++);
dispatcher.listen('multi_event', (_, __) => callCount2++);
await dispatcher.dispatch('multi_event');
expect(callCount1, equals(1));
expect(callCount2, equals(1));
});
});
}
abstract class MQClientWrapper {
Stream<Message> fetchQueue(String queueId);
void sendMessage({
required Message message,
String? exchangeName,
String? routingKey,
});
String declareQueue(String queueId);
void declareExchange({
required String exchangeName,
required ExchangeType exchangeType,
});
void bindQueue({
required String queueId,
required String exchangeName,
String? bindingKey,
});
void close();
}
class RealMQClientWrapper implements MQClientWrapper {
final MQClient _client;
RealMQClientWrapper(this._client);
@override
Stream<Message> fetchQueue(String queueId) => _client.fetchQueue(queueId);
@override
void sendMessage({
required Message message,
String? exchangeName,
String? routingKey,
}) =>
_client.sendMessage(
message: message,
exchangeName: exchangeName,
routingKey: routingKey,
);
@override
String declareQueue(String queueId) => _client.declareQueue(queueId);
@override
void declareExchange({
required String exchangeName,
required ExchangeType exchangeType,
}) =>
_client.declareExchange(
exchangeName: exchangeName,
exchangeType: exchangeType,
);
@override
void bindQueue({
required String queueId,
required String exchangeName,
String? bindingKey,
}) =>
_client.bindQueue(
queueId: queueId,
exchangeName: exchangeName,
bindingKey: bindingKey,
);
@override
void close() => _client.close();
}
class MockMQClient implements MQClient {
Map<String, List<Message>> queuedMessages = {};
int _messageIdCounter = 0;
void queueMessage(String queueName, Message message) {
queuedMessages.putIfAbsent(queueName, () => []).add(message);
print(
'Queued message. Queue $queueName now has ${queuedMessages[queueName]?.length} messages');
}
@override
String declareQueue(String queueId) {
queuedMessages[queueId] = [];
return queueId;
}
@override
void deleteQueue(String queueId) {
queuedMessages.remove(queueId);
}
@override
Stream<Message> fetchQueue(String queueId) {
print('Fetching queue: $queueId');
return Stream.fromIterable(queuedMessages[queueId] ?? []);
}
@override
void sendMessage({
required Message message,
String? exchangeName,
String? routingKey,
}) {
print('Sending message to queue: $routingKey');
final newMessage = Message(
payload: message.payload,
headers: message.headers,
timestamp: message.timestamp,
id: 'msg_${_messageIdCounter++}',
);
queueMessage(routingKey ?? '', newMessage);
}
@override
Message? getLatestMessage(String queueId) {
final messages = queuedMessages[queueId];
return messages?.isNotEmpty == true ? messages!.last : null;
}
@override
void bindQueue({
required String queueId,
required String exchangeName,
String? bindingKey,
}) {
// Implement if needed for your tests
}
@override
void unbindQueue({
required String queueId,
required String exchangeName,
String? bindingKey,
}) {
// Implement if needed for your tests
}
@override
void declareExchange({
required String exchangeName,
required ExchangeType exchangeType,
}) {
// Implement if needed for your tests
}
@override
void deleteExchange(String exchangeName) {
// Implement if needed for your tests
}
@override
List<String> listQueues() {
return queuedMessages.keys.toList();
}
@override
void close() {
queuedMessages.clear();
}
@override
void deleteMessage(String queueId, Message message) {
print('Deleting message from queue: $queueId');
queuedMessages[queueId]?.removeWhere((m) => m.id == message.id);
print(
'After deletion, queue $queueId has ${queuedMessages[queueId]?.length} messages');
}
}
class BroadcastTestEvent implements AppEvent, ShouldBroadcast {
@override
List<Object?> get props => [];
@override
bool? get stringify => true;
@override
DateTime get timestamp => DateTime.now();
}
class QueueTestEvent implements AppEvent, ShouldQueue {
@override
List<Object?> get props => [];
@override
bool? get stringify => true;
@override
DateTime get timestamp => DateTime.now();
}
// This is a simple implementation of Reflector that does nothing
class EmptyReflector implements Reflector {
const EmptyReflector();
@override
ReflectedType reflectType(Type type) {
throw UnimplementedError();
}
@override
ReflectedInstance reflectInstance(Object object) {
throw UnimplementedError();
}
@override
ReflectedType reflectFutureOf(Type type) {
throw UnimplementedError();
}
@override
String? getName(Symbol symbol) {
// TODO: implement getName
throw UnimplementedError();
}
@override
ReflectedClass? reflectClass(Type clazz) {
// TODO: implement reflectClass
throw UnimplementedError();
}
@override
ReflectedFunction? reflectFunction(Function function) {
// TODO: implement reflectFunction
throw UnimplementedError();
}
}

7
packages/pipeline/.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/
# Avoid committing pubspec.lock for library packages; see
# https://dart.dev/guides/libraries/private-files#pubspeclock.
pubspec.lock

View file

@ -0,0 +1,3 @@
## 1.0.0
- Initial version.

View file

@ -0,0 +1,10 @@
The MIT License (MIT)
The Laravel Framework is Copyright (c) Taylor Otwell
The Fabric Framework is Copyright (c) Vieo, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

380
packages/pipeline/README.md Normal file
View file

@ -0,0 +1,380 @@
<p align="center"><a href="https://protevus.com" target="_blank"><img src="https://git.protevus.com/protevus/branding/raw/branch/main/protevus-logo-bg.png"></a></p>
# Platform Pipeline
A Laravel-compatible pipeline implementation in Dart, providing a robust way to pass objects through a series of operations.
[![Pub Version](https://img.shields.io/pub/v/platform_pipeline)]()
[![Build Status](https://img.shields.io/github/workflow/status/platform/pipeline/tests)]()
## Table of Contents
- [Overview](#overview)
- [Features](#features)
- [Requirements](#requirements)
- [Installation](#installation)
- [Usage](#usage)
- [Basic Usage](#basic-usage)
- [Class-Based Pipes](#class-based-pipes)
- [Invokable Classes](#invokable-classes)
- [Using Different Method Names](#using-different-method-names)
- [Passing Parameters to Pipes](#passing-parameters-to-pipes)
- [Early Pipeline Termination](#early-pipeline-termination)
- [Conditional Pipeline Execution](#conditional-pipeline-execution)
- [Advanced Usage](#advanced-usage)
- [Working with Objects](#working-with-objects)
- [Async Operations](#async-operations)
- [Laravel API Compatibility](#laravel-api-compatibility)
- [Comparison with Laravel](#comparison-with-laravel)
- [Troubleshooting](#troubleshooting)
- [Testing](#testing)
- [Contributing](#contributing)
- [License](#license)
## Overview
Platform Pipeline is a 100% API-compatible port of Laravel's Pipeline to Dart. It allows you to pass an object through a series of operations (pipes) in a fluent, maintainable way. Each pipe can examine, modify, or replace the object before passing it to the next pipe in the sequence.
## Features
- 💯 100% Laravel Pipeline API compatibility
- 🔄 Support for class-based and callable pipes
- 🎯 Dependency injection through container integration
- ⚡ Async operation support
- 🔀 Conditional pipeline execution
- 🎭 Method name customization via `via()`
- 🎁 Parameter passing to pipes
- 🛑 Early pipeline termination
- 🧪 Comprehensive test coverage
## Requirements
- Dart SDK: >=2.17.0 <4.0.0
- platform_container: ^1.0.0
## Installation
Add this to your package's `pubspec.yaml` file:
```yaml
dependencies:
platform_pipeline: ^1.0.0
```
## Usage
### Basic Usage
```dart
import 'package:platform_pipeline/pipeline.dart';
import 'package:platform_container/container.dart';
void main() async {
// Create a container instance
var container = Container();
// Create a pipeline
var result = await Pipeline(container)
.send('Hello')
.through([
(String value, next) => next(value + ' World'),
(String value, next) => next(value + '!'),
])
.then((value) => value);
print(result); // Outputs: Hello World!
}
```
### Class-Based Pipes
```dart
class UppercasePipe {
Future<String> handle(String value, Function next) async {
return next(value.toUpperCase());
}
}
class AddExclamationPipe {
Future<String> handle(String value, Function next) async {
return next(value + '!');
}
}
void main() async {
var container = Container();
var result = await Pipeline(container)
.send('hello')
.through([
UppercasePipe(),
AddExclamationPipe(),
])
.then((value) => value);
print(result); // Outputs: HELLO!
}
```
### Invokable Classes
```dart
class TransformPipe {
Future<String> call(String value, Function next) async {
return next(value.toUpperCase());
}
}
void main() async {
var container = Container();
var result = await Pipeline(container)
.send('hello')
.through([TransformPipe()])
.then((value) => value);
print(result); // Outputs: HELLO
}
```
### Using Different Method Names
```dart
class CustomPipe {
Future<String> transform(String value, Function next) async {
return next(value.toUpperCase());
}
}
void main() async {
var container = Container();
var result = await Pipeline(container)
.send('hello')
.through([CustomPipe()])
.via('transform')
.then((value) => value);
print(result); // Outputs: HELLO
}
```
### Passing Parameters to Pipes
```dart
class PrefixPipe {
Future<String> handle(
String value,
Function next, [
String prefix = '',
]) async {
return next('$prefix$value');
}
}
void main() async {
var container = Container();
container.registerFactory<PrefixPipe>((c) => PrefixPipe());
var pipeline = Pipeline(container);
pipeline.registerPipeType('PrefixPipe', PrefixPipe);
var result = await pipeline
.send('World')
.through('PrefixPipe:Hello ')
.then((value) => value);
print(result); // Outputs: Hello World
}
```
### Early Pipeline Termination
```dart
void main() async {
var container = Container();
var result = await Pipeline(container)
.send('hello')
.through([
(value, next) => 'TERMINATED', // Pipeline stops here
(value, next) => next('NEVER REACHED'),
])
.then((value) => value);
print(result); // Outputs: TERMINATED
}
```
### Conditional Pipeline Execution
```dart
void main() async {
var container = Container();
var shouldTransform = true;
var result = await Pipeline(container)
.send('hello')
.when(() => shouldTransform, (Pipeline pipeline) {
pipeline.pipe([
(value, next) => next(value.toUpperCase()),
]);
})
.then((value) => value);
print(result); // Outputs: HELLO
}
```
## Advanced Usage
### Working with Objects
```dart
class User {
String name;
int age;
User(this.name, this.age);
}
class AgeValidationPipe {
Future<User> handle(User user, Function next) async {
if (user.age < 18) {
throw Exception('User must be 18 or older');
}
return next(user);
}
}
class NameFormattingPipe {
Future<User> handle(User user, Function next) async {
user.name = user.name.trim().toLowerCase();
return next(user);
}
}
void main() async {
var container = Container();
var user = User('John Doe ', 20);
try {
user = await Pipeline(container)
.send(user)
.through([
AgeValidationPipe(),
NameFormattingPipe(),
])
.then((value) => value);
print('${user.name} is ${user.age} years old');
// Outputs: john doe is 20 years old
} catch (e) {
print('Validation failed: $e');
}
}
```
### Async Operations
```dart
class AsyncTransformPipe {
Future<String> handle(String value, Function next) async {
// Simulate async operation
await Future.delayed(Duration(seconds: 1));
return next(value.toUpperCase());
}
}
void main() async {
var container = Container();
var result = await Pipeline(container)
.send('hello')
.through([AsyncTransformPipe()])
.then((value) => value);
print(result); // Outputs after 1 second: HELLO
}
```
## Laravel API Compatibility
This package maintains 100% API compatibility with Laravel's Pipeline implementation. All Laravel Pipeline features are supported:
- `send()` - Set the object being passed through the pipeline
- `through()` - Set the array of pipes
- `pipe()` - Push additional pipes onto the pipeline
- `via()` - Set the method to call on the pipes
- `then()` - Run the pipeline with a final destination callback
- `thenReturn()` - Run the pipeline and return the result
## Comparison with Laravel
| Feature | Laravel | Platform Pipeline |
|---------|---------|------------------|
| API Methods | ✓ | ✓ |
| Container Integration | ✓ | ✓ |
| Pipe Types | Class, Callable | Class, Callable |
| Async Support | ✗ | ✓ |
| Type Safety | ✗ | ✓ |
| Parameter Passing | ✓ | ✓ |
| Early Termination | ✓ | ✓ |
| Method Customization | ✓ | ✓ |
| Conditional Execution | ✓ | ✓ |
## Troubleshooting
### Common Issues
1. Container Not Provided
```dart
// ❌ Wrong
var pipeline = Pipeline(null);
// ✓ Correct
var container = Container();
var pipeline = Pipeline(container);
```
2. Missing Type Registration
```dart
// ❌ Wrong
pipeline.through('CustomPipe:param');
// ✓ Correct
pipeline.registerPipeType('CustomPipe', CustomPipe);
pipeline.through('CustomPipe:param');
```
3. Incorrect Method Name
```dart
// ❌ Wrong
class CustomPipe {
void process(value, next) {} // Wrong method name
}
// ✓ Correct
class CustomPipe {
void handle(value, next) {} // Default method name
}
// Or specify the method name:
pipeline.via('process').through([CustomPipe()]);
```
## Testing
Run the tests with:
```bash
dart test
```
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
This package is open-sourced software licensed under the MIT license.

View file

@ -0,0 +1,30 @@
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.
include: package:lints/recommended.yaml
# Uncomment the following section to specify additional rules.
# linter:
# rules:
# - camel_case_types
# analyzer:
# exclude:
# - path/to/excluded/files/**
# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints
# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options

View file

@ -0,0 +1,38 @@
import 'package:platform_core/core.dart';
import 'package:platform_core/http.dart';
import 'package:platform_container/mirrors.dart';
import 'package:platform_pipeline/pipeline.dart';
class AsyncGreetingPipe {
Future<dynamic> handle(String input, Function next) async {
await Future.delayed(Duration(seconds: 1));
return next('Hello, $input');
}
}
class AsyncExclamationPipe {
Future<dynamic> handle(String input, Function next) async {
await Future.delayed(Duration(seconds: 1));
return next('$input!');
}
}
void main() async {
var app = Application(reflector: MirrorsReflector());
var http = PlatformHttp(app);
app.container.registerSingleton((c) => Pipeline(c));
app.get('/', (req, res) async {
var pipeline = app.container.make<Pipeline>();
var result = await pipeline
.send('World')
.through(['AsyncGreetingPipe', 'AsyncExclamationPipe']).then(
(result) => result.toUpperCase());
res.write(result); // Outputs: "HELLO, WORLD!" (after 2 seconds)
});
await http.startServer('localhost', 3000);
print('Server started on http://localhost:3000');
}

View file

@ -0,0 +1,36 @@
import 'package:platform_core/core.dart';
import 'package:platform_core/http.dart';
import 'package:platform_container/mirrors.dart';
import 'package:platform_pipeline/pipeline.dart';
class GreetingPipe {
dynamic handle(String input, Function next) {
return next('Hello, $input');
}
}
class ExclamationPipe {
dynamic handle(String input, Function next) {
return next('$input!');
}
}
void main() async {
var app = Application(reflector: MirrorsReflector());
var http = PlatformHttp(app);
app.container.registerSingleton((c) => Pipeline(c));
app.get('/', (req, res) async {
var pipeline = app.container.make<Pipeline>();
var result = await pipeline
.send('World')
.through(['GreetingPipe', 'ExclamationPipe']).then(
(result) => result.toUpperCase());
res.write(result); // Outputs: "HELLO, WORLD!"
});
await http.startServer('localhost', 3000);
print('Server started on http://localhost:3000');
}

View file

@ -0,0 +1,34 @@
import 'package:platform_core/core.dart';
import 'package:platform_core/http.dart';
import 'package:platform_container/mirrors.dart';
import 'package:platform_pipeline/pipeline.dart';
class ErrorPipe {
dynamic handle(String input, Function next) {
throw Exception('Simulated error');
}
}
void main() async {
var app = Application(reflector: MirrorsReflector());
var http = PlatformHttp(app);
app.container.registerSingleton((c) => Pipeline(c));
app.get('/', (req, res) async {
var pipeline = app.container.make<Pipeline>();
try {
await pipeline
.send('World')
.through(['ErrorPipe']).then((result) => result.toUpperCase());
} catch (e) {
res.write('Error occurred: ${e.toString()}');
return;
}
res.write('This should not be reached');
});
await http.startServer('localhost', 3000);
print('Server started on http://localhost:3000');
}

View file

@ -0,0 +1,35 @@
import 'package:platform_core/core.dart';
import 'package:platform_core/http.dart';
import 'package:platform_container/mirrors.dart';
import 'package:platform_pipeline/pipeline.dart';
class GreetingPipe {
dynamic handle(String input, Function next) {
return next('Hello, $input');
}
}
void main() async {
var app = Application(reflector: MirrorsReflector());
var http = PlatformHttp(app);
app.container.registerSingleton((c) => Pipeline(c));
app.get('/', (req, res) async {
var pipeline = app.container.make<Pipeline>();
var result = await pipeline.send('World').through([
'GreetingPipe',
(String input, Function next) => next('$input!'),
(String input, Function next) async {
await Future.delayed(Duration(seconds: 1));
return next(input.toUpperCase());
},
]).then((result) => 'Final result: $result');
res.write(
result); // Outputs: "Final result: HELLO, WORLD!" (after 1 second)
});
await http.startServer('localhost', 3000);
print('Server started on http://localhost:3000');
}

View file

@ -0,0 +1,5 @@
library;
export 'src/pipeline.dart';
export 'src/conditionable.dart';
export 'src/pipeline_contract.dart';

View file

@ -0,0 +1,16 @@
/// Provides conditional execution methods for the pipeline.
mixin Conditionable<T> {
T when(bool Function() callback, void Function(T) callback2) {
if (callback()) {
callback2(this as T);
}
return this as T;
}
T unless(bool Function() callback, void Function(T) callback2) {
if (!callback()) {
callback2(this as T);
}
return this as T;
}
}

View file

@ -0,0 +1,241 @@
import 'dart:async';
import 'dart:mirrors';
import 'package:platform_container/container.dart';
import 'package:logging/logging.dart';
import 'pipeline_contract.dart';
import 'conditionable.dart';
/// Defines the signature for a pipe function.
typedef PipeFunction = FutureOr<dynamic> Function(
dynamic passable, FutureOr<dynamic> Function(dynamic) next);
/// The primary class for building and executing pipelines.
class Pipeline with Conditionable<Pipeline> implements PipelineContract {
/// The container implementation.
Container? _container;
final Map<String, Type> _typeMap = {};
/// The object being passed through the pipeline.
dynamic _passable;
/// The array of class pipes.
final List<dynamic> _pipes = [];
/// The method to call on each pipe.
String _method = 'handle';
/// Logger for the pipeline.
final Logger _logger = Logger('Pipeline');
/// Create a new class instance.
Pipeline(this._container);
void registerPipeType(String name, Type type) {
_typeMap[name] = type;
}
/// Set the object being sent through the pipeline.
@override
Pipeline send(dynamic passable) {
_passable = passable;
return this;
}
/// Set the array of pipes.
@override
Pipeline through(dynamic pipes) {
if (_container == null) {
throw Exception(
'A container instance has not been passed to the Pipeline.');
}
_pipes.addAll(pipes is Iterable ? pipes.toList() : [pipes]);
return this;
}
/// Push additional pipes onto the pipeline.
@override
Pipeline pipe(dynamic pipes) {
if (_container == null) {
throw Exception(
'A container instance has not been passed to the Pipeline.');
}
_pipes.addAll(pipes is Iterable ? pipes.toList() : [pipes]);
return this;
}
/// Set the method to call on the pipes.
@override
Pipeline via(String method) {
_method = method;
return this;
}
/// Run the pipeline with a final destination callback.
@override
Future<dynamic> then(FutureOr<dynamic> Function(dynamic) destination) async {
if (_container == null) {
throw Exception(
'A container instance has not been passed to the Pipeline.');
}
var pipeline = (dynamic passable) async => await destination(passable);
for (var pipe in _pipes.reversed) {
var next = pipeline;
pipeline = (dynamic passable) async {
return await carry(pipe, passable, next);
};
}
return await pipeline(_passable);
}
/// Run the pipeline and return the result.
@override
Future<dynamic> thenReturn() async {
return then((passable) => passable);
}
/// Get a Closure that represents a slice of the application onion.
Future<dynamic> carry(dynamic pipe, dynamic passable, Function next) async {
try {
if (pipe is Function) {
return await pipe(passable, next);
}
if (pipe is String) {
if (_container == null) {
throw Exception('Container is null, cannot resolve pipe: $pipe');
}
final parts = parsePipeString(pipe);
final pipeClass = parts[0];
final parameters = parts.length > 1 ? parts.sublist(1) : [];
Type? pipeType;
if (_typeMap.containsKey(pipeClass)) {
pipeType = _typeMap[pipeClass];
} else {
// Try to resolve from mirrors
try {
for (var lib in currentMirrorSystem().libraries.values) {
for (var decl in lib.declarations.values) {
if (decl is ClassMirror &&
decl.simpleName == Symbol(pipeClass)) {
pipeType = decl.reflectedType;
break;
}
}
if (pipeType != null) break;
}
} catch (_) {}
if (pipeType == null) {
throw Exception('Type not registered for pipe: $pipe');
}
}
var instance = _container?.make(pipeType);
if (instance == null) {
throw Exception('Unable to resolve pipe: $pipe');
}
return await invokeMethod(
instance, _method, [passable, next, ...parameters]);
}
if (pipe is Type) {
if (_container == null) {
throw Exception('Container is null, cannot resolve pipe type');
}
var instance = _container?.make(pipe);
if (instance == null) {
throw Exception('Unable to resolve pipe type: $pipe');
}
return await invokeMethod(instance, _method, [passable, next]);
}
// Handle instance of a class
if (pipe is Object) {
return await invokeMethod(pipe, _method, [passable, next]);
}
throw Exception('Unsupported pipe type: ${pipe.runtimeType}');
} catch (e) {
return handleException(passable, e);
}
}
/// Parse full pipe string to get name and parameters.
List<String> parsePipeString(String pipe) {
var parts = pipe.split(':');
return [parts[0], if (parts.length > 1) ...parts[1].split(',')];
}
/// Get the array of configured pipes.
List<dynamic> pipes() {
return List.unmodifiable(_pipes);
}
/// Get the container instance.
Container getContainer() {
if (_container == null) {
throw Exception(
'A container instance has not been passed to the Pipeline.');
}
return _container!;
}
/// Set the container instance.
Pipeline setContainer(Container container) {
_container = container;
return this;
}
/// Handle the value returned from each pipe before passing it to the next.
dynamic handleCarry(dynamic carry) {
if (carry is Future) {
return carry.then((value) => value ?? _passable);
}
return carry ?? _passable;
}
Future<dynamic> invokeMethod(
dynamic instance, String methodName, List<dynamic> arguments) async {
// First try call() for invokable objects
if (instance is Function) {
return await instance(arguments[0], arguments[1]);
}
var instanceMirror = reflect(instance);
// Check for call method first (invokable objects)
var callSymbol = Symbol('call');
if (instanceMirror.type.declarations.containsKey(callSymbol)) {
var result = instanceMirror.invoke(callSymbol, arguments);
return await result.reflectee;
}
// Then try the specified method
var methodSymbol = Symbol(methodName);
if (!instanceMirror.type.declarations.containsKey(methodSymbol)) {
throw Exception('Method $methodName not found on instance: $instance');
}
var result = instanceMirror.invoke(methodSymbol, arguments);
return await result.reflectee;
}
/// Handle the given exception.
dynamic handleException(dynamic passable, Object e) {
if (e is Exception && e.toString().contains('Container is null')) {
throw Exception(
'A container instance has not been passed to the Pipeline.');
}
_logger.severe('Exception occurred in pipeline', e);
throw e;
}
}

View file

@ -0,0 +1,9 @@
/// Represents a series of "pipes" through which an object can be passed.
abstract class PipelineContract {
PipelineContract send(dynamic passable);
PipelineContract through(dynamic pipes);
PipelineContract pipe(dynamic pipes);
PipelineContract via(String method);
Future<dynamic> then(dynamic Function(dynamic) destination);
Future<dynamic> thenReturn();
}

View file

@ -0,0 +1,19 @@
name: platform_pipeline
description: The Pipeline Package for the Protevus Platform
version: 0.0.1
homepage: https://protevus.com
documentation: https://docs.protevus.com
repository: https://github.com/protevus/platform
environment:
sdk: ^3.4.2
# Add regular dependencies here.
dependencies:
platform_container: ^9.0.0
platform_core: ^9.0.0
logging: ^1.1.0
dev_dependencies:
lints: ^3.0.0
test: ^1.24.0

View file

@ -0,0 +1,258 @@
import 'package:platform_container/container.dart';
import 'package:platform_pipeline/pipeline.dart';
import 'package:test/test.dart';
// Test pipe classes to match Laravel's test classes
class PipelineTestPipeOne {
static String? testPipeOne;
Future<dynamic> handle(dynamic piped, Function next) async {
testPipeOne = piped.toString();
return next(piped);
}
Future<dynamic> differentMethod(dynamic piped, Function next) async {
return next(piped);
}
}
class PipelineTestPipeTwo {
static String? testPipeOne;
Future<dynamic> call(dynamic piped, Function next) async {
testPipeOne = piped.toString();
return next(piped);
}
}
class PipelineTestParameterPipe {
static List<String>? testParameters;
Future<dynamic> handle(dynamic piped, Function next,
[String? parameter1, String? parameter2]) async {
testParameters = [
if (parameter1 != null) parameter1,
if (parameter2 != null) parameter2
];
return next(piped);
}
}
void main() {
group('Laravel Pipeline Tests', () {
late Container container;
late Pipeline pipeline;
setUp(() {
container = Container(const EmptyReflector());
pipeline = Pipeline(container);
// Register test classes with container
container
.registerFactory<PipelineTestPipeOne>((c) => PipelineTestPipeOne());
container
.registerFactory<PipelineTestPipeTwo>((c) => PipelineTestPipeTwo());
container.registerFactory<PipelineTestParameterPipe>(
(c) => PipelineTestParameterPipe());
// Register types with pipeline
pipeline.registerPipeType('PipelineTestPipeOne', PipelineTestPipeOne);
pipeline.registerPipeType('PipelineTestPipeTwo', PipelineTestPipeTwo);
pipeline.registerPipeType(
'PipelineTestParameterPipe', PipelineTestParameterPipe);
// Reset static test variables
PipelineTestPipeOne.testPipeOne = null;
PipelineTestPipeTwo.testPipeOne = null;
PipelineTestParameterPipe.testParameters = null;
});
test('Pipeline basic usage', () async {
String? testPipeTwo;
final pipeTwo = (dynamic piped, Function next) {
testPipeTwo = piped.toString();
return next(piped);
};
final result = await Pipeline(container)
.send('foo')
.through([PipelineTestPipeOne(), pipeTwo]).then((piped) => piped);
expect(result, equals('foo'));
expect(PipelineTestPipeOne.testPipeOne, equals('foo'));
expect(testPipeTwo, equals('foo'));
});
test('Pipeline usage with objects', () async {
final result = await Pipeline(container)
.send('foo')
.through([PipelineTestPipeOne()]).then((piped) => piped);
expect(result, equals('foo'));
expect(PipelineTestPipeOne.testPipeOne, equals('foo'));
});
test('Pipeline usage with invokable objects', () async {
final result = await Pipeline(container)
.send('foo')
.through([PipelineTestPipeTwo()]).then((piped) => piped);
expect(result, equals('foo'));
expect(PipelineTestPipeTwo.testPipeOne, equals('foo'));
});
test('Pipeline usage with callable', () async {
String? testPipeOne;
final function = (dynamic piped, Function next) {
testPipeOne = 'foo';
return next(piped);
};
var result = await Pipeline(container)
.send('foo')
.through([function]).then((piped) => piped);
expect(result, equals('foo'));
expect(testPipeOne, equals('foo'));
testPipeOne = null;
result =
await Pipeline(container).send('bar').through(function).thenReturn();
expect(result, equals('bar'));
expect(testPipeOne, equals('foo'));
});
test('Pipeline usage with pipe', () async {
final object = {'value': 0};
final function = (dynamic obj, Function next) {
obj['value']++;
return next(obj);
};
final result = await Pipeline(container)
.send(object)
.through([function]).pipe([function]).then((piped) => piped);
expect(result, equals(object));
expect(object['value'], equals(2));
});
test('Pipeline usage with invokable class', () async {
final result = await Pipeline(container)
.send('foo')
.through([PipelineTestPipeTwo()]).then((piped) => piped);
expect(result, equals('foo'));
expect(PipelineTestPipeTwo.testPipeOne, equals('foo'));
});
test('Then method is not called if the pipe returns', () async {
String thenValue = '(*_*)';
String secondValue = '(*_*)';
final result = await Pipeline(container).send('foo').through([
(value, next) => 'm(-_-)m',
(value, next) {
secondValue = 'm(-_-)m';
return next(value);
},
]).then((piped) {
thenValue = '(0_0)';
return piped;
});
expect(result, equals('m(-_-)m'));
// The then callback is not called
expect(thenValue, equals('(*_*)'));
// The second pipe is not called
expect(secondValue, equals('(*_*)'));
});
test('Then method input value', () async {
String? pipeReturn;
String? thenArg;
final result = await Pipeline(container).send('foo').through([
(value, next) async {
final nextValue = await next('::not_foo::');
pipeReturn = nextValue;
return 'pipe::$nextValue';
}
]).then((piped) {
thenArg = piped;
return 'then$piped';
});
expect(result, equals('pipe::then::not_foo::'));
expect(thenArg, equals('::not_foo::'));
});
test('Pipeline usage with parameters', () async {
final parameters = ['one', 'two'];
final result = await Pipeline(container)
.send('foo')
.through('PipelineTestParameterPipe:${parameters.join(',')}')
.then((piped) => piped);
expect(result, equals('foo'));
expect(PipelineTestParameterPipe.testParameters, equals(parameters));
});
test('Pipeline via changes the method being called on the pipes', () async {
final result = await Pipeline(container)
.send('data')
.through(PipelineTestPipeOne())
.via('differentMethod')
.then((piped) => piped);
expect(result, equals('data'));
});
test('Pipeline throws exception on resolve without container', () async {
expect(
() => Pipeline(null)
.send('data')
.through(PipelineTestPipeOne())
.then((piped) => piped),
throwsA(isA<Exception>().having(
(e) => e.toString(),
'message',
contains(
'A container instance has not been passed to the Pipeline'))));
});
test('Pipeline thenReturn method runs pipeline then returns passable',
() async {
final result = await Pipeline(container)
.send('foo')
.through([PipelineTestPipeOne()]).thenReturn();
expect(result, equals('foo'));
expect(PipelineTestPipeOne.testPipeOne, equals('foo'));
});
test('Pipeline conditionable', () async {
var result = await Pipeline(container).send('foo').when(() => true,
(Pipeline pipeline) {
pipeline.pipe([PipelineTestPipeOne()]);
}).then((piped) => piped);
expect(result, equals('foo'));
expect(PipelineTestPipeOne.testPipeOne, equals('foo'));
PipelineTestPipeOne.testPipeOne = null;
result = await Pipeline(container).send('foo').when(() => false,
(Pipeline pipeline) {
pipeline.pipe([PipelineTestPipeOne()]);
}).then((piped) => piped);
expect(result, equals('foo'));
expect(PipelineTestPipeOne.testPipeOne, isNull);
});
});
}

View file

@ -0,0 +1,106 @@
import 'package:test/test.dart';
import 'package:platform_core/core.dart';
import 'package:platform_container/container.dart';
import 'package:platform_container/mirrors.dart';
import 'package:platform_pipeline/pipeline.dart';
class AddExclamationPipe {
Future<String> handle(String input, Function next) async {
return await next('$input!');
}
}
class UppercasePipe {
Future<String> handle(String input, Function next) async {
return await next(input.toUpperCase());
}
}
void main() {
late Application app;
late Container container;
late Pipeline pipeline;
setUp(() {
app = Application(reflector: MirrorsReflector());
container = app.container;
container.registerSingleton(AddExclamationPipe());
container.registerSingleton(UppercasePipe());
pipeline = Pipeline(container);
pipeline.registerPipeType('AddExclamationPipe', AddExclamationPipe);
pipeline.registerPipeType('UppercasePipe', UppercasePipe);
});
test('Pipeline should process simple string pipes', () async {
var result = await pipeline.send('hello').through(
['AddExclamationPipe', 'UppercasePipe']).then((res) async => res);
expect(result, equals('HELLO!'));
});
test('Pipeline should process function pipes', () async {
var result = await pipeline.send('hello').through([
(String input, Function next) async {
var result = await next('$input, WORLD');
return result;
},
(String input, Function next) async {
var result = await next(input.toUpperCase());
return result;
},
]).then((res) async => res as String);
expect(result, equals('HELLO, WORLD'));
});
test('Pipeline should handle mixed pipe types', () async {
var result = await pipeline.send('hello').through([
'AddExclamationPipe',
(String input, Function next) async {
var result = await next(input.toUpperCase());
return result;
},
]).then((res) async => res as String);
expect(result, equals('HELLO!'));
});
test('Pipeline should handle async pipes', () async {
var result = await pipeline.send('hello').through([
'UppercasePipe',
(String input, Function next) async {
await Future.delayed(Duration(milliseconds: 100));
return next('$input, WORLD');
},
]).then((res) async => res as String);
expect(result, equals('HELLO, WORLD'));
});
test('Pipeline should throw exception for unresolvable pipe', () {
expect(
() => pipeline
.send('hello')
.through(['NonExistentPipe']).then((res) => res),
throwsA(isA<Exception>()),
);
});
test('Pipeline should allow chaining of pipes', () async {
var result = await pipeline
.send('hello')
.pipe('AddExclamationPipe')
.pipe('UppercasePipe')
.then((res) async => res as String);
expect(result, equals('HELLO!'));
});
test('Pipeline should respect the order of pipes', () async {
var result1 = await pipeline
.send('hello')
.through(['AddExclamationPipe', 'UppercasePipe']).then((res) => res);
var result2 = await pipeline
.send('hello')
.through(['UppercasePipe', 'AddExclamationPipe']).then((res) => res);
expect(result1, equals('HELLO!'));
expect(result2, equals('HELLO!!'));
expect(result1, isNot(equals(result2)));
});
}

7
packages/process/.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/
# Avoid committing pubspec.lock for library packages; see
# https://dart.dev/guides/libraries/private-files#pubspeclock.
pubspec.lock

View file

@ -0,0 +1,3 @@
## 1.0.0
- Initial version.

View file

@ -0,0 +1,10 @@
The MIT License (MIT)
The Laravel Framework is Copyright (c) Taylor Otwell
The Fabric Framework is Copyright (c) Vieo, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -0,0 +1 @@
<p align="center"><a href="https://protevus.com" target="_blank"><img src="https://git.protevus.com/protevus/branding/raw/branch/main/protevus-logo-bg.png"></a></p>

View file

@ -0,0 +1,30 @@
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.
include: package:lints/recommended.yaml
# Uncomment the following section to specify additional rules.
# linter:
# rules:
# - camel_case_types
# analyzer:
# exclude:
# - path/to/excluded/files/**
# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints
# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options

View file

@ -0,0 +1,36 @@
// examples/basic_process/main.dart
import 'package:platform_core/core.dart';
import 'package:platform_core/http.dart';
import 'package:platform_process/angel3_process.dart';
import 'package:logging/logging.dart';
import 'package:platform_container/mirrors.dart';
void main() async {
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) {
print('${record.level.name}: ${record.time}: ${record.message}');
});
// Create an Angel application with MirrorsReflector
var app = Application(reflector: MirrorsReflector());
var http = PlatformHttp(app);
// Use dependency injection for ProcessManager
app.container.registerSingleton(ProcessManager());
app.get('/', (req, res) async {
// Use the ioc function to get the ProcessManager instance
var processManager = await req.container?.make<ProcessManager>();
var process = await processManager?.start(
'example_process',
'echo',
['Hello, Angel3 Process!'],
);
var result = await process?.run();
res.writeln('Process output: ${result?.output.trim()}');
});
await http.startServer('localhost', 3000);
print('Server listening at http://localhost:3000');
}

View file

@ -0,0 +1,37 @@
// examples/process_pipeline/main.dart
import 'package:platform_core/core.dart';
import 'package:platform_core/http.dart';
import 'package:platform_process/angel3_process.dart';
import 'package:logging/logging.dart';
import 'package:platform_container/mirrors.dart';
void main() async {
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) {
print('${record.level.name}: ${record.time}: ${record.message}');
});
// Create an Angel application with MirrorsReflector
var app = Application(reflector: MirrorsReflector());
var http = PlatformHttp(app);
// Register ProcessManager as a singleton in the container
app.container.registerSingleton(ProcessManager());
app.get('/', (req, res) async {
// Use dependency injection to get the ProcessManager instance
var processManager = await req.container?.make<ProcessManager>();
var processes = [
angel3Process('echo', ['Hello']),
angel3Process('sed', ['s/Hello/Greetings/']),
angel3Process('tr', ['[:lower:]', '[:upper:]']),
];
var result = await processManager?.pipeline(processes);
res.writeln('Pipeline output: ${result?.output.trim()}');
});
await http.startServer('localhost', 3000);
print('Server listening at http://localhost:3000');
}

View file

@ -0,0 +1,37 @@
// examples/process_pool/main.dart
import 'package:platform_core/core.dart';
import 'package:platform_core/http.dart';
import 'package:platform_process/angel3_process.dart';
import 'package:logging/logging.dart';
import 'package:platform_container/mirrors.dart';
void main() async {
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) {
print('${record.level.name}: ${record.time}: ${record.message}');
});
// Create an Angel application with MirrorsReflector
var app = Application(reflector: MirrorsReflector());
var http = PlatformHttp(app);
// Register ProcessManager as a singleton in the container
app.container.registerSingleton(ProcessManager());
app.get('/', (req, res) async {
// Use dependency injection to get the ProcessManager instance
var processManager = await req.container?.make<ProcessManager>();
var processes =
List.generate(5, (index) => angel3Process('echo', ['Process $index']));
var results = await processManager?.pool(processes, concurrency: 3);
var output = results
?.map((result) =>
'${result.process.command} output: ${result.output.trim()}')
.join('\n');
res.write(output);
});
await http.startServer('localhost', 3000);
print('Server listening at http://localhost:3000');
}

View file

@ -0,0 +1,67 @@
// examples/web_server_with_processes/main.dart
import 'package:platform_core/core.dart';
import 'package:platform_core/http.dart';
import 'package:platform_process/angel3_process.dart';
import 'package:file/local.dart';
import 'package:logging/logging.dart';
import 'package:angel3_mustache/angel3_mustache.dart';
import 'package:platform_container/mirrors.dart';
void main() async {
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) {
print('${record.level.name}: ${record.time}: ${record.message}');
});
// Create an Angel application with MirrorsReflector
var app = Application(reflector: MirrorsReflector());
var http = PlatformHttp(app);
// Register dependencies in the container
app.container.registerSingleton(const LocalFileSystem());
app.container.registerSingleton(ProcessManager());
// Set up the view renderer
var fs = await app.container.make<LocalFileSystem>();
var viewsDirectory = fs.directory('views');
//await app.configure(mustache(viewsDirectory));
app.get('/', (req, res) async {
await res.render('index');
});
app.post('/run-process', (req, res) async {
var body = await req.bodyAsMap;
var command = body['command'] as String?;
var args = (body['args'] as String?)?.split(' ') ?? [];
if (command == null || command.isEmpty) {
throw PlatformHttpException.badRequest(message: 'Command is required');
}
// Use dependency injection to get the ProcessManager instance
var processManager = await req.container?.make<ProcessManager>();
var process = await processManager?.start(
'user_process',
command,
args,
);
var result = await process?.run();
await res.json({
'output': result?.output.trim(),
'exitCode': result?.exitCode,
});
});
app.fallback((req, res) => throw PlatformHttpException.notFound());
app.errorHandler = (e, req, res) {
res.writeln('Error: ${e.message}');
return false;
};
await http.startServer('localhost', 3000);
print('Server listening at http://localhost:3000');
}

View file

@ -0,0 +1,39 @@
<!-- examples/web_server_with_processes/views/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Angel3 Process Example</title>
</head>
<body>
<h1>Run a Process</h1>
<form id="processForm">
<label for="command">Command:</label>
<input type="text" id="command" name="command" required>
<br>
<label for="args">Arguments:</label>
<input type="text" id="args" name="args">
<br>
<button type="submit">Run Process</button>
</form>
<div id="output"></div>
<script>
document.getElementById('processForm').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const response = await fetch('/run-process', {
method: 'POST',
body: formData
});
const result = await response.json();
document.getElementById('output').innerHTML = `
<h2>Output:</h2>
<pre>${result.output}</pre>
<p>Exit Code: ${result.exitCode}</p>
`;
});
</script>
</body>
</html>

View file

@ -0,0 +1,7 @@
library;
export 'src/process.dart';
export 'src/process_helper.dart';
export 'src/process_manager.dart';
export 'src/process_pipeline.dart';
export 'src/process_pool.dart';

Some files were not shown because too many files have changed in this diff Show more