CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-tsyringe

Lightweight dependency injection container for JavaScript/TypeScript

Pending
Overview
Eval results
Files

lifecycle-management.mddocs/

Lifecycle Management

Comprehensive lifecycle management system with multiple scopes, registration options, disposable resource handling, and interception capabilities for advanced dependency behavior customization.

Capabilities

Lifecycle Scopes

Enumeration defining different lifecycle behaviors for dependency instances.

/**
 * Lifecycle enumeration defining dependency instance lifecycles
 * Controls when and how instances are created and reused
 */
enum Lifecycle {
  /** New instance created on every resolution */
  Transient = 0,
  /** Single instance shared across entire application */
  Singleton = 1,
  /** Single instance per resolution chain (same resolve() call) */
  ResolutionScoped = 2,
  /** Single instance per container (including child containers) */
  ContainerScoped = 3
}

Usage Examples:

// Transient - new instance every time
container.register("TransientService", TransientService, {
  lifecycle: Lifecycle.Transient
});

// Singleton - shared across application
container.register("ConfigService", ConfigService, {
  lifecycle: Lifecycle.Singleton
});

// Resolution scoped - shared within single resolve call
container.register("RequestContext", RequestContext, {
  lifecycle: Lifecycle.ResolutionScoped
});

// Container scoped - shared within container instance
container.register("CacheService", CacheService, {
  lifecycle: Lifecycle.ContainerScoped
});

// Default is Transient if not specified
container.register("DefaultService", DefaultService);

Registration Options

Configuration object for specifying lifecycle and other registration behavior.

/**
 * Options for dependency registration
 * Currently supports lifecycle configuration
 */
interface RegistrationOptions {
  lifecycle: Lifecycle;
}

Usage Examples:

// Explicit lifecycle specification
const singletonOptions: RegistrationOptions = {
  lifecycle: Lifecycle.Singleton
};

container.register("DatabaseService", DatabaseService, singletonOptions);

// Inline options
container.register("CacheService", CacheService, {
  lifecycle: Lifecycle.ContainerScoped
});

// Factory with lifecycle
container.register("Logger", {
  useFactory: (container) => new Logger(container.resolve("LogLevel"))
}, { lifecycle: Lifecycle.Singleton });

Disposable Resources

Interface and utilities for managing resource disposal and cleanup.

/**
 * Interface for disposable resources
 * Enables automatic cleanup when container is disposed
 */
interface Disposable {
  dispose(): Promise<void> | void;
}

/**
 * Type guard to check if value implements Disposable
 * @param value - Value to check
 * @returns True if value has dispose method
 */  
function isDisposable(value: any): value is Disposable;

Usage Examples:

// Disposable service
class DatabaseService implements Disposable {
  private connection: Connection;
  
  constructor() {
    this.connection = createConnection();
  }
  
  async dispose() {
    await this.connection.close();
    console.log("Database connection closed");
  }
}

// File service with synchronous disposal
class FileService implements Disposable {
  private fileHandle: FileHandle;
  
  dispose() {
    this.fileHandle.close();
    console.log("File handle closed");
  }
}

// Register disposable services
container.registerSingleton(DatabaseService);
container.registerSingleton(FileService);

// Check if disposable
const service = container.resolve(DatabaseService);
if (isDisposable(service)) {
  console.log("Service is disposable");
}

// Cleanup all disposable resources
await container.dispose();

Interception System

Advanced interception system for customizing dependency resolution behavior with pre and post resolution hooks.

/**
 * Options for resolution interceptors
 * Controls when interceptors are executed
 */
interface InterceptorOptions {
  frequency: Frequency;
}

/**
 * Frequency enumeration for interceptor execution
 * Controls how often interceptors run
 */
type Frequency = "Always" | "Once";

/**
 * Callback executed before dependency resolution
 * @param token - Token being resolved
 * @param resolutionType - Type of resolution (Single or All)
 */
type PreResolutionInterceptorCallback<T> = (
  token: InjectionToken<T>,
  resolutionType: ResolutionType
) => void;

/**
 * Callback executed after dependency resolution
 * @param token - Token that was resolved
 * @param result - Resolved instance(s)
 * @param resolutionType - Type of resolution (Single or All)
 */
type PostResolutionInterceptorCallback<T> = (
  token: InjectionToken<T>,
  result: T | T[],
  resolutionType: ResolutionType
) => void;

/**
 * Resolution type enumeration
 * Indicates whether single or multiple instances were resolved
 */
type ResolutionType = "Single" | "All";

Usage Examples:

// Pre-resolution logging
container.beforeResolution("UserService", (token, resolutionType) => {
  console.log(`About to resolve ${String(token)} as ${resolutionType}`);
  performance.mark(`resolve-${String(token)}-start`);
});

// Post-resolution logging with performance measurement
container.afterResolution("UserService", (token, result, resolutionType) => {
  performance.mark(`resolve-${String(token)}-end`);
  performance.measure(
    `resolve-${String(token)}`,
    `resolve-${String(token)}-start`,
    `resolve-${String(token)}-end`
  );
  console.log(`Resolved ${String(token)}:`, result);
});

// One-time initialization interceptor
container.afterResolution("DatabaseService", (token, result) => {
  console.log("Database service initialized for first time");
  (result as DatabaseService).runMigrations();
}, { frequency: "Once" });

// Always-running validation interceptor
container.afterResolution("ConfigService", (token, result) => {
  const config = result as ConfigService;
  if (!config.isValid()) {
    throw new Error("Invalid configuration detected");
  }
}, { frequency: "Always" });

// Multi-resolution interceptor
container.afterResolution("Plugin", (token, result, resolutionType) => {
  if (resolutionType === "All") {
    const plugins = result as Plugin[];
    console.log(`Loaded ${plugins.length} plugins`);
  }
});

Container Lifecycle

Container-level lifecycle management including reset, instance clearing, and disposal.

/**
 * Clear all registrations from container
 * Does not affect parent containers in hierarchy
 */
reset(): void;

/**
 * Clear all cached instances but keep registrations
 * Forces fresh instance creation on next resolve
 */
clearInstances(): void;

/**
 * Dispose container and all registered disposable resources
 * Calls dispose() on all instances implementing Disposable
 * @returns Promise resolving when all disposals complete
 */
dispose(): Promise<void>;

Usage Examples:

// Setup container with services
container.registerSingleton(DatabaseService);
container.registerSingleton(CacheService);
container.register("TempService", TempService);

// Clear cached instances (services will be recreated on next resolve)
container.clearInstances();

// Reset all registrations (complete cleanup)
container.reset();

// Proper application shutdown
class Application {
  async shutdown() {
    console.log("Shutting down application...");
    
    // Dispose all resources
    await container.dispose();
    
    console.log("Application shutdown complete");
  }
}

// Graceful shutdown handling
process.on('SIGTERM', async () => {
  const app = container.resolve(Application);
  await app.shutdown();
  process.exit(0);
});

Child Container Lifecycle

Hierarchical container management with inheritance and scoped lifecycles.

/**
 * Create child container inheriting from parent
 * Child containers can override parent registrations
 * Container-scoped instances are shared within child container
 * @returns New child DependencyContainer instance
 */
createChildContainer(): DependencyContainer;

Usage Examples:

// Parent container setup
container.register("GlobalService", GlobalService, { lifecycle: Lifecycle.Singleton });
container.register("SharedService", SharedService, { lifecycle: Lifecycle.ContainerScoped });

// Child container with overrides
const childContainer = container.createChildContainer();
childContainer.register("SharedService", ChildSpecificService); // Override parent
childContainer.register("ChildOnlyService", ChildOnlyService);

// Resolution behavior
const globalFromParent = container.resolve("GlobalService");
const globalFromChild = childContainer.resolve("GlobalService");
// Same instance (singleton spans containers)

const sharedFromParent = container.resolve("SharedService"); // Original SharedService
const sharedFromChild = childContainer.resolve("SharedService"); // ChildSpecificService

// Child cleanup
await childContainer.dispose(); // Only disposes child-specific resources

Types

// Lifecycle enumeration
enum Lifecycle {
  Transient = 0,
  Singleton = 1,
  ResolutionScoped = 2,
  ContainerScoped = 3
}

// Registration configuration
interface RegistrationOptions {
  lifecycle: Lifecycle;
}

// Disposable interface
interface Disposable {
  dispose(): Promise<void> | void;
}

// Interception types
interface InterceptorOptions {
  frequency: Frequency;
}

type Frequency = "Always" | "Once";
type ResolutionType = "Single" | "All";

type PreResolutionInterceptorCallback<T> = (
  token: InjectionToken<T>,
  resolutionType: ResolutionType
) => void;

type PostResolutionInterceptorCallback<T> = (
  token: InjectionToken<T>,
  result: T | T[],
  resolutionType: ResolutionType
) => void;

Install with Tessl CLI

npx tessl i tessl/npm-tsyringe

docs

decorators.md

dependency-container.md

factories.md

index.md

lazy-loading.md

lifecycle-management.md

providers.md

tile.json