register method

Future<void> register(
  1. AppModule module
)

Register a module with full lifecycle management

Lifecycle:

  1. Check dependencies
  2. Call onBind(AirDI)
  3. Call onInit(AirDI)
  4. Add to registered modules

Implementation

Future<void> register(AppModule module) async {
  if (_modules.any((m) => m.id == module.id)) {
    debugPrint('ModuleManager: Module ${module.id} already registered');
    return;
  }

  // Check required dependencies (with version if specified)
  for (final depSpec in module.dependencies) {
    // Parse dependency specification (e.g., "auth:^1.0.0" or just "auth")
    final parts = depSpec.split(':');
    final depId = parts[0];
    final versionReq = parts.length > 1 ? parts[1] : null;

    final depModule = _modules.where((m) => m.id == depId).firstOrNull;
    if (depModule == null) {
      throw StateError(
        'ModuleManager: Module "${module.id}" requires "$depId" '
        'but it is not registered. Register dependencies first.',
      );
    }

    // Verify version compatibility if version requirement is specified
    if (versionReq != null) {
      if (!_checkVersionCompatibility(depModule.version, versionReq)) {
        debugPrint(
          '\x1B[33m[ModuleManager] Warning: Module "${module.id}" requires "$depId" '
          'version $versionReq, but ${depModule.version} is installed.\x1B[0m',
        );
      }
    }
  }

  // Log optional dependencies that are missing
  for (final depSpec in module.optionalDependencies) {
    final parts = depSpec.split(':');
    final depId = parts[0];
    if (!_modules.any((m) => m.id == depId)) {
      debugPrint(
        '\x1B[33m[ModuleManager] Info: Optional dependency "$depId" for "${module.id}" '
        'is not available.\x1B[0m',
      );
    }
  }

  // Create context for the module
  final context = ModuleContext(moduleId: module.id, moduleName: module.name);

  try {
    // Get DI instance
    final di = AirDI();

    // Register dependencies before initialization
    module.onBind(di);

    // Self-initialization without main.dart knowing details
    await module.onInit(di);

    // Only add to modules list AFTER successful initialization
    _modules.add(module);
    _contexts[module.id] = context;

    // Emit module installed event
    EventBus().emit(
      ModuleInstalledEvent(
        sourceModuleId: 'system',
        installedModuleId: module.id,
        installedModuleName: module.name,
        version: module.version,
      ),
    );

    debugPrint('ModuleManager: Module ${module.id} registered successfully');
    notifyListeners();
  } catch (e, st) {
    module.onError(e, st);
    debugPrint('ModuleManager: Failed to initialize module ${module.id}: $e');
    rethrow;
  }
}