platform/doc/core_package_specification.md
2024-11-25 20:33:45 -07:00

14 KiB

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

Core Features

1. Application

/// 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

/// 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

/// 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

/// 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

/// 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

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

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

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

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
  2. Check Laravel Compatibility Roadmap
  3. Follow Testing Guide
  4. Use Foundation Integration Guide
  5. Review Container Package Specification
  6. Review Events Package Specification

2. Implementation Process

For each core feature:

  1. Write tests following Testing Guide
  2. Implement following framework patterns
  3. Document following Getting Started Guide
  4. Integrate following Foundation Integration Guide

3. Quality Requirements

All implementations must:

  1. Pass all tests (see Testing Guide)
  2. Follow framework patterns
  3. Follow integration patterns (see Foundation Integration Guide)
  4. Support dependency injection (see Container Package Specification)
  5. Support event system (see Events Package Specification)

4. Integration Considerations

When implementing core features:

  1. Follow patterns in Foundation Integration Guide
  2. Use framework patterns consistently
  3. Use testing approaches from Testing Guide
  4. Follow development setup in Getting Started Guide

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

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

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