# JetLeaf

🍃 JetLeaf is a comprehensive, enterprise-grade Dart backend framework that brings Jetleaf-like dependency injection, configuration management, and application lifecycle to Dart server-side development.

JetLeaf provides a complete foundation for building scalable, maintainable backend applications with powerful features like annotation-driven configuration, automatic component scanning, profile-based environments, and event-driven architecture.

- Homepage: https://jetleaf.hapnium.com
- Repository: https://github.com/jetleaf/jetleaf
- License: See `LICENSE`

## Contents

- Features
- Install
- Quick Start
- Core Concepts
  - Application Bootstrap
  - Dependency Injection
  - Configuration Management
  - Component Scanning
  - Lifecycle Management
  - Profile Management
- Usage
  - Creating an Application
  - Configuration Classes
  - Services and Components
  - Property Injection
  - Conditional Configuration
  - Application Runners
  - Event Handling
- Configuration Files
- CLI Tools
- API Reference
- Examples
- Changelog
- Contributing
- Compatibility

## Features

### Core Framework

- 🚀 Application Bootstrap – `JetApplication` for rapid application startup with `@JetLeafApplication`
- 💉 Dependency Injection – Full IoC container with constructor, field, and setter injection
- 📦 Component Scanning – Automatic discovery of `@Component`, `@Service`, `@Repository`, `@Controller`
- ⚙️ Configuration Management – YAML, JSON, Properties, and Dart-based configuration
- 🔄 Lifecycle Management – `@PostConstruct`, `@PreDestroy`, and context lifecycle hooks
- 🎯 Profile Management – Environment-specific configuration with `@Profile`
- 🔀 Conditional Processing – `@Conditional`, `@ConditionalOnProperty`, `@ConditionalOnPod`
- 📡 Event System – Application-wide event publishing and listening
- 🌍 Internationalization – Multi-locale message resolution
- 🎨 Banner Display – Customizable startup banners

### Advanced Features

- Auto-Configuration – `@EnableAutoConfiguration` for framework and library auto-setup
- Property Binding – `@Value` for property injection with placeholders and expressions
- Type Conversion – Automatic type conversion for properties and dependencies
- Circular Reference Handling – Smart resolution of circular dependencies
- Lazy Initialization – `@Lazy` for deferred pod creation
- Primary Pods – `@Primary` for disambiguation when multiple candidates exist
- Ordered Components – `@Order` for component ordering
- Exit Code Management – Graceful shutdown with exit code generation
- Exception Handling – Comprehensive exception reporting and handling

## Install

Add to your `pubspec.yaml`:

    dependencies:
      jetleaf:
        hosted: https://onepub.dev/api/fahnhnofly/
        version: ^1.0.0

Minimum SDK: Dart ^3.9.0

Import:

    import 'package:jetleaf/jetleaf.dart';

## Quick Start

### 1. Create Your Application

    import 'package:jetleaf/jetleaf.dart';

    void main(List<String> args) {
        JetApplication.run(MyApplication, args);
    }

    @JetLeafApplication()
    class MyApplication {}

### 2. Define Services

    @Service()
    class UserService {
      final UserRepository repository;

      UserService(this.repository);

      Future<User> findById(String id) async {
        return await repository.findById(id);
      }
    }

    @Repository()
    class UserRepository {
      Future<User> findById(String id) async {
        // Database access logic
        return User(id: id, name: 'John Doe');
      }
    }

### 3. Create Configuration

    @Configuration()
    class DatabaseConfig {
      @Pod()
      DatabaseConnection databaseConnection() {
        return DatabaseConnection(
          host: 'localhost',
          port: 5432,
          database: 'myapp',
        );
      }
    }

### 4. Run Your Application

    dart run lib/main.dart

## Core Concepts

### Application Bootstrap

JetLeaf applications start with the `JetApplication` class, which handles:

- Runtime Detection: AOT vs JIT compilation detection
- Bootstrap Context: Early component registration
- Environment Setup: Property sources and profile activation
- Context Creation: Application context building and configuration
- Lifecycle Management: Startup, refresh, and shutdown orchestration

    void main(List<String> args) async {
      final app = JetApplication(MyApplication);

      // Custom configuration
      app.setBannerMode(BannerMode.CONSOLE);
      app.setAdditionalProfiles({'production'});
      app.setLazyInitialization(true);

      // Add initializers
      app.addInitializer((context) {
        // Custom initialization
      });

      // Launch
      final context = await app.create(args, null);
    }

### Dependency Injection

JetLeaf provides comprehensive dependency injection with multiple strategies:

Constructor Injection (recommended):

    @Service()
    class OrderService {
      final UserService userService;
      final PaymentService paymentService;

      OrderService(this.userService, this.paymentService);
    }

Field Injection:

    @Service()
    class OrderService {
      @Autowired()
      late UserService userService;

      @Autowired()
      late PaymentService paymentService;
    }

Auto-Injection:

    @Service()
    @RequiredAll()
    class OrderService {
      late UserService userService;       // Auto-injected
      late PaymentService paymentService; // Auto-injected
    }

### Configuration Management

JetLeaf supports multiple configuration formats:

YAML Configuration (`application.yaml`):

    server:
      port: 8080
      host: localhost

    database:
      url: postgresql://localhost:5432/myapp
      maxConnections: 20

    logging:
      level: INFO

Properties Configuration (`application.properties`):

    server.port=8080
    server.host=localhost
    database.url=postgresql://localhost:5432/myapp
    database.maxConnections=20

Dart Configuration:

    @Configuration()
    class AppConfig {
      @Pod()
      ServerConfig serverConfig() {
        return ServerConfig(port: 8080, host: 'localhost');
      }
    }

### Component Scanning

JetLeaf automatically discovers and registers components:

Stereotype Annotations:

- `@Component` – Generic component
- `@Service` – Business logic layer
- `@Repository` – Data access layer
- `@Controller` – Presentation/routing layer

    @Service()
    class EmailService {
      Future<void> sendEmail(String to, String subject, String body) async {
        // Email sending logic
      }
    }

    @Repository()
    class UserRepository {
      Future<User> save(User user) async {
        // Database persistence
      }
    }

    @Controller()
    class UserController {
      final UserService userService;

      UserController(this.userService);
    }

### Lifecycle Management

Control component lifecycle with annotations:

    @Service()
    class DatabaseService {
      late DatabaseConnection connection;

      @PostConstruct()
      Future<void> initialize() async {
        connection = await DatabaseConnection.connect();
        print('Database connected');
      }

      @PreDestroy()
      Future<void> cleanup() async {
        await connection.close();
        print('Database connection closed');
      }
    }

### Profile Management

Configure environment-specific behavior:

    @Configuration()
    @Profile('development')
    class DevConfig {
      @Pod()
      Logger logger() => Logger.debug();
    }

    @Configuration()
    @Profile('production')
    class ProdConfig {
      @Pod()
      Logger logger() => Logger.production();
    }

Activate profiles via command line:

    dart run lib/main.dart --jetleaf.profiles.active=production

## Usage

### Creating an Application

    import 'package:jetleaf/jetleaf.dart';

    @JetLeafApplication()
    class MyApplication {
      static void main(List<String> args) {
        JetApplication.run(MyApplication, args);
      }
    }

With custom configuration:

    void main(List<String> args) async {
      final app = JetApplication(MyApplication);

      // Banner configuration
      app.setBannerMode(BannerMode.CONSOLE);

      // Profile activation
      app.setAdditionalProfiles({'production', 'cloud'});

      // Performance tuning
      app.setLazyInitialization(true);
      app.setAllowCircularReferences(false);

      // Custom initializers
      app.addInitializer((context) async {
        // Pre-refresh initialization
      });

      // Event listeners
      app.addListener(MyApplicationListener());

      // Launch application
      final context = await app.create(args, null);
    }

### Configuration Classes

    @Configuration()
    class DatabaseConfig {
      @Value('#{database.url}')
      late String databaseUrl;

      @Value('#{database.maxConnections:20}')
      late int maxConnections;

      @Pod()
      DatabaseConnection primaryDatabase() {
        return DatabaseConnection(
          url: databaseUrl,
          maxConnections: maxConnections,
        );
      }

      @Pod('readOnlyDatabase')
      @Scope('prototype')
      DatabaseConnection readOnlyDatabase() {
        return DatabaseConnection(
          url: databaseUrl,
          readOnly: true,
        );
      }
    }

### Services and Components

    @Service()
    class UserService {
      final UserRepository repository;
      final EmailService emailService;
      final Logger logger;

      UserService(this.repository, this.emailService, this.logger);

      Future<User> registerUser(CreateUserRequest request) async {
        logger.info('Registering user: ${request.email}');

        final user = await repository.save(User.fromRequest(request));

        await emailService.sendWelcomeEmail(user.email);

        return user;
      }
    }

    @Repository()
    class UserRepository {
      final DatabaseConnection db;

      UserRepository(this.db);

      Future<User> save(User user) async {
        // Database persistence logic
        return user;
      }

      Future<User?> findByEmail(String email) async {
        // Database query logic
        return null;
      }
    }

### Property Injection

    @Component()
    class ApiClient {
      @Value('#{api.baseUrl}')
      late String baseUrl;

      @Value('#{api.timeout:30}')
      late int timeout;

      @Value('#{api.apiKey}')
      late String apiKey;

      Future<Response> get(String endpoint) async {
        final url = '$baseUrl$endpoint';
        // HTTP request logic
      }
    }

### Conditional Configuration

    // Conditional on property
    @ConditionalOnProperty(
      prefix: 'cache',
      names: ['enabled'],
      havingValue: 'true',
    )
    @Configuration()
    class CacheConfig {
      @Pod()
      CacheManager cacheManager() => RedisCacheManager();
    }

    // Conditional on pod existence
    @ConditionalOnPod(DatabaseConnection)
    @Service()
    class DatabaseMigrationService {
      final DatabaseConnection db;

      DatabaseMigrationService(this.db);

      @PostConstruct()
      Future<void> runMigrations() async {
        // Migration logic
      }
    }

    // Conditional on missing pod
    @ConditionalOnMissingPod(CacheManager)
    @Configuration()
    class DefaultCacheConfig {
      @Pod()
      CacheManager cacheManager() => InMemoryCacheManager();
    }

### Application Runners

Execute logic after application startup:

    @Component()
    class DataInitializer implements ApplicationRunner {
      final UserRepository userRepository;

      DataInitializer(this.userRepository);

      @override
      Future<void> run(ApplicationArguments args) async {
        print('Initializing data...');
        await userRepository.seedDefaultUsers();
      }
    }

    @Component()
    class CommandLineProcessor implements CommandLineRunner {
      @override
      Future<void> run(List<String> args) async {
        print('Processing command line arguments: $args');
      }
    }

### Event Handling

    // Define custom event
    class UserRegisteredEvent extends ApplicationEvent {
      final User user;

      UserRegisteredEvent(Object source, this.user) : super(source);
    }

    // Publish event
    @Service()
    class UserService {
      final ApplicationContext context;

      UserService(this.context);

      Future<User> registerUser(CreateUserRequest request) async {
        final user = await userRepository.save(request);

        // Publish event
        await context.publishEvent(UserRegisteredEvent(this, user));

        return user;
      }
    }

    // Listen to event
    @Component()
    class UserEventListener implements ApplicationEventListener<UserRegisteredEvent> {
      final EmailService emailService;

      UserEventListener(this.emailService);

      @override
      Future<void> onApplicationEvent(UserRegisteredEvent event) async {
        await emailService.sendWelcomeEmail(event.user.email);
      }
    }

## Configuration Files

JetLeaf supports multiple configuration file formats in the `resources/` directory:

### application.yaml

    jetleaf:
      application:
        name: MyApplication
        version: 1.0.0
      profiles:
        active: development

    server:
      port: 8080
      host: 0.0.0.0

    database:
      url: postgresql://localhost:5432/myapp
      username: admin
      password: secret
      maxConnections: 20

    logging:
      level: INFO
      pattern: "%d{yyyy-MM-dd HH:mm:ss} [%level] %logger - %msg"

### application.properties

    jetleaf.application.name=MyApplication
    jetleaf.application.version=1.0.0
    jetleaf.profiles.active=development

    server.port=8080
    server.host=0.0.0.0

    database.url=postgresql://localhost:5432/myapp
    database.username=admin
    database.password=secret

### Profile-Specific Configuration

- `application-dev.yaml` – Development profile
- `application-prod.yaml` – Production profile
- `application-test.yaml` – Test profile

## CLI Tools

JetLeaf provides CLI tools for development and deployment:

### Development

    # Run in development mode with hot reload
    jl dev

    # Run with specific profile
    jl dev --profile=development

### Build and Deploy

    # Generate bootstrap file
    jl generate

    # Build production executable
    jl build

    # Generate and build
    jl serve

    # Run production build
    dart run build/main.dill

## API Reference

### Main Exports (`lib/jetleaf.dart`)

- Bootstrap: `BootstrapContext`
- Application: `JetApplication`, `JetLeafApplication`
- Lang: Re-exports from `jetleaf_lang` (Class, Annotation, etc.)
- Environment: Re-exports from `jetleaf_env` (Environment, PropertyResolver, etc.)
- Core: Re-exports from `jetleaf_core` (ApplicationContext, annotations, etc.)
- Convert: Re-exports from `jetleaf_convert` (ConversionService, Converters, etc.)
- Pod: Re-exports from `jetleaf_pod` (PodFactory, PodDefinition, etc.)

### Key Classes

- `JetApplication`: Main application bootstrap class
- `@JetLeafApplication`: Application entry point annotation
- `@EnableAutoConfiguration`: Enable auto-configuration
- `ApplicationContext`: Central application container
- `ConfigurableApplicationContext`: Configurable context interface

### Annotations

- Configuration: `@Configuration`, `@AutoConfiguration`, `@Pod`
- Stereotypes: `@Component`, `@Service`, `@Repository`, `@Controller`
- Dependency Injection: `@Autowired`, `@Value`, `@Qualifier`, `@RequiredAll`
- Lifecycle: `@PostConstruct`, `@PreDestroy`, `@Lazy`
- Conditional: `@Conditional`, `@ConditionalOnProperty`, `@ConditionalOnPod`
- Others: `@Primary`, `@Scope`, `@Order`, `@Profile`

## Examples

### REST API Application

    @JetLeafApplication()
    class ApiApplication {
      static void main(List<String> args) {
        JetApplication.run(ApiApplication, args);
      }
    }

    @Controller()
    class UserController {
      final UserService userService;

      UserController(this.userService);

      Future<Response> getUser(String id) async {
        final user = await userService.findById(id);
        return Response.ok(user);
      }
    }

### Microservice with Database

    @JetLeafApplication()
    @EnableAutoConfiguration()
    class MicroserviceApplication {
      static void main(List<String> args) {
        JetApplication.run(MicroserviceApplication, args);
      }
    }

    @Configuration()
    class DatabaseConfig {
      @Pod()
      DatabaseConnection database(
        @Value('#{database.url}') String url,
        @Value('#{database.maxConnections}') int maxConnections,
      ) {
        return DatabaseConnection(url: url, maxConnections: maxConnections);
      }
    }

### Scheduled Tasks

    @Service()
    class ScheduledTaskService {
      @Scheduled(cron: '0 0 * * * *') // Every hour
      Future<void> hourlyTask() async {
        print('Running hourly task');
      }

      @Scheduled(fixedDelay: Duration(minutes: 5))
      Future<void> periodicTask() async {
        print('Running every 5 minutes');
      }
    }

## Changelog

See `CHANGELOG.md`.

## Contributing

Issues and PRs are welcome at the GitHub repository.

1. Fork and create a feature branch.
2. Add tests for new functionality.
3. Run `dart test` and ensure lints pass.
4. Open a PR with a concise description and examples.

## Compatibility

- Dart SDK: `>=3.9.0 <4.0.0`
- Depends on: `jetleaf_lang`, `jetleaf_logging`, `jetleaf_convert`, `jetleaf_core`, `jetleaf_utils`, `jetleaf_env`, `jetleaf_pod`

Built with 🍃 by the JetLeaf team.

## Libraries

jetleaf