run method

Future<BootstrapReport> run({
  1. required Map<String, dynamic> config,
  2. List<BackendAdapter> adapters = const [],
  3. List<FeaturePlugin Function()> plugins = const [],
})

Executes the full startup sequence and returns a BootstrapReport.

Provide config as the raw configuration map (typically parsed from environment.json). Pass adapters and plugins in the order they should be registered — registration order determines hook priority tie-breaking and plugin dependency resolution.

The returned BootstrapReport is safe to inspect immediately after await; it is never null and always contains timing data for every step that completed, even if later steps failed.

// Inside AppBootstrap.initState
final report = await MooseBootstrapper(appContext: ctx).run(
  config: {'plugins': {'products': {'active': true}}},
  adapters: [WooCommerceAdapter()],
  plugins: [() => ProductsPlugin()],
);
setState(() => _bootstrapReport = report);

See also:

Implementation

Future<BootstrapReport> run({
  required Map<String, dynamic> config,
  List<BackendAdapter> adapters = const [],
  List<FeaturePlugin Function()> plugins = const [],
}) async {
  final sw = Stopwatch()..start();
  final timings = <String, Duration>{};
  final startTimings = <String, Duration>{};
  final failures = <String, Object>{};

  // Step 1: Initialize configuration.
  appContext.configManager.initialize(config);

  // Step 2: Initialize the scoped persistent cache layer.
  await appContext.cache.initPersistent();

  // Step 2b: Restore last-known auth state from persistent cache so the UI
  //          can show user-specific content on the first frame, before any
  //          adapter's authStateChanges stream confirms the session.
  await appContext.restoreAuthState();

  // Step 3: Wire AppNavigator to the scoped EventBus so that navigation
  //         events flow through this context's bus (not a global singleton).
  AppNavigator.setEventBus(appContext.eventBus);

  // Step 4: Register and initialize adapters.
  for (final adapter in adapters) {
    try {
      await appContext.adapterRegistry.registerAdapter(() => adapter);
    } catch (e) {
      failures['adapter:${adapter.name}'] = e;
      appContext.logger.error(
        'Adapter "${adapter.name}" failed to register',
        e,
      );
    }
  }

  // Step 5: Register plugins (sync — injects appContext, calls onRegister).
  for (final factory in plugins) {
    FeaturePlugin? plugin;
    try {
      plugin = factory();
      appContext.pluginRegistry.register(plugin, appContext: appContext);
    } catch (e) {
      final name = plugin?.name ?? 'unknown';
      failures['plugin:$name'] = e;
      appContext.logger.error('Plugin "$name" failed to register', e);
    }
  }

  // Step 6: Initialize all registered plugins (async).
  try {
    await appContext.pluginRegistry.initAll(timings: timings);
  } catch (e) {
    failures['plugin:initAll'] = e;
    appContext.logger.error('Plugin init phase failed', e);
  }

  // Step 7: Start all registered plugins (async).
  try {
    await appContext.pluginRegistry.startAll(timings: startTimings);
  } catch (e) {
    failures['plugin:startAll'] = e;
    appContext.logger.error('Plugin start phase failed', e);
  }

  sw.stop();
  appContext.logger.info(
    'Bootstrap complete in ${sw.elapsed.inMilliseconds}ms '
    '(${timings.length} plugins inited, ${startTimings.length} plugins started, ${failures.length} failures)',
  );

  appContext.logger.debug('Plugin timings: $timings');
  appContext.logger.debug('Plugin start timings: $startTimings');

  return BootstrapReport(
    totalTime: sw.elapsed,
    pluginTimings: timings,
    pluginStartTimings: startTimings,
    failures: failures,
  );
}