Skip to main content

Type Definition

type AppContext = AppConfig & {
  emitter: Emitter;
  container: Container;
  rootPath: PathResolver;
  runtimePath: PathResolver;
}
The AppContext type provides a context object that extensions and bootstrap functions can use to access application configuration, the dependency injection container, event emitter, and path resolution utilities.

Properties

name

readonly name: string
name
string
The name of the application.

debug

readonly debug?: boolean
debug
boolean
Whether debug mode is enabled. Defaults to false if not specified.

timezone

readonly timezone?: string
timezone
string
The application’s timezone. Defaults to "UTC" if not specified.

emitter

emitter: Emitter
emitter
Emitter
The application’s event emitter instance from @resolid/event.Use this to:
  • Emit custom application events
  • Listen for application lifecycle events (e.g., 'app:ready')
  • Implement event-driven communication between extensions

container

container: Container
container
Container
The dependency injection container instance from @resolid/di.Use this to:
  • Retrieve services by token using container.get(token)
  • Dynamically register additional providers
  • Access the DI container during extension initialization

rootPath

rootPath: PathResolver
rootPath
PathResolver
Path resolver function for paths relative to the application root directory.Type signature:
type PathResolver = (...paths: string[]) => string
  • Resolves paths from the current working directory (cwd)
  • Automatically converts backslashes to forward slashes
  • Returns absolute paths

runtimePath

runtimePath: PathResolver
runtimePath
PathResolver
Path resolver function for paths relative to the application’s runtime directory ({root}/runtime).Type signature:
type PathResolver = (...paths: string[]) => string
  • Resolves paths from {cwd}/runtime
  • Automatically converts backslashes to forward slashes
  • Returns absolute paths

Usage in Extensions

Extension Creator

Extension creator functions receive the AppContext as a parameter:
import { type ExtensionCreator } from '@resolid/core';

const createConfigExtension = (): ExtensionCreator => {
  return (context) => {
    // Access context properties
    const configPath = context.rootPath('config', 'app.json');
    
    return {
      name: 'config',
      providers: [
        {
          token: CONFIG,
          factory: () => {
            const config = loadConfig(configPath);
            
            if (context.debug) {
              console.log('Loaded config:', config);
            }
            
            return config;
          }
        }
      ]
    };
  };
};

Bootstrap Function

Bootstrap functions receive the AppContext as their only parameter:
import type { Extension } from '@resolid/core';

const databaseExtension: Extension = {
  name: 'database',
  providers: [
    {
      token: DATABASE,
      factory: () => new DatabaseClient()
    }
  ],
  bootstrap: async (context) => {
    // Get service from container
    const db = context.container.get(DATABASE);
    
    // Use path resolvers
    const migrationsDir = context.rootPath('database', 'migrations');
    
    // Connect and run migrations
    await db.connect();
    await db.runMigrations(migrationsDir);
    
    // Log in debug mode
    if (context.debug) {
      console.log(`[${context.name}] Database connected`);
    }
    
    // Set up event listeners
    context.emitter.on('app:shutdown', async () => {
      await db.disconnect();
    });
  }
};

Complete Example

import { createApp, type ExtensionCreator } from '@resolid/core';

const LOGGER = Symbol('LOGGER');
const CACHE = Symbol('CACHE');

const createLoggerExtension = (): ExtensionCreator => {
  return (context) => ({
    name: 'logger',
    providers: [
      {
        token: LOGGER,
        factory: () => ({
          log: (message: string) => {
            const timestamp = new Date().toISOString();
            console.log(`[${context.name}] ${timestamp}: ${message}`);
          }
        })
      }
    ],
    bootstrap: async (context) => {
      const logger = context.container.get(LOGGER);
      const logDir = context.runtimePath('logs');
      
      logger.log(`Application starting`);
      logger.log(`Log directory: ${logDir}`);
      logger.log(`Timezone: ${context.timezone}`);
      logger.log(`Debug mode: ${context.debug}`);
      
      // Listen for app ready event
      context.emitter.on('app:ready', () => {
        logger.log('Application ready!');
      });
      
      // Listen for custom events
      context.emitter.on('cache:clear', () => {
        logger.log('Cache cleared');
      });
    }
  });
};

const cacheExtension: Extension = {
  name: 'cache',
  providers: [
    {
      token: CACHE,
      factory: () => {
        const store = new Map();
        return {
          get: (key: string) => store.get(key),
          set: (key: string, value: any) => store.set(key, value),
          clear: () => store.clear()
        };
      }
    }
  ],
  bootstrap: async (context) => {
    const cache = context.container.get(CACHE);
    
    // Store app metadata in cache
    cache.set('app:name', context.name);
    cache.set('app:timezone', context.timezone);
    cache.set('app:started', new Date().toISOString());
    
    // Listen for clear event
    context.emitter.on('cache:clear', () => {
      cache.clear();
    });
  }
};

const app = await createApp({
  name: 'MyApp',
  debug: true,
  timezone: 'America/New_York',
  extensions: [
    createLoggerExtension(),
    cacheExtension
  ],
  expose: {
    logger: LOGGER,
    cache: CACHE
  }
});

await app.run();

// Trigger custom event
app.emitter.emit('cache:clear');

// Access exposed services
app.$.logger.log('Hello from exposed service!');

See Also

  • Extension - Extension interface that uses AppContext
  • App - The App class that creates the context
  • createApp - Factory function for creating applications

Build docs developers (and LLMs) love