import 'reflect-metadata'; import { ProviderScope, type ProviderInjectedRecord, type ProviderRecord, type Token } from '@frontmcp/di'; import { FrontMcpServer, type ProviderEntry, type ProviderRegistryInterface, type ProviderType, type RegistryKind, type RegistryType, type ScopeEntry } from '../common'; import { type DistributedEnabled } from '../common/types/options/transport'; import { RegistryAbstract, type RegistryBuildMapResult } from '../regsitry'; import { type ProviderViews } from './provider.types'; /** * Configuration options for ProviderRegistry. */ export interface ProviderRegistryOptions { /** * Distributed mode setting. * Controls provider caching behavior for serverless/distributed deployments. */ distributedMode?: DistributedEnabled; /** * Override for provider session caching. * When undefined, defaults based on distributedMode setting. */ providerCaching?: boolean; } export default class ProviderRegistry extends RegistryAbstract implements ProviderRegistryInterface { private readonly parentProviders?; /** used to track which registry provided which token */ private readonly providedBy; /** topo order (deps first) */ private order; private registries; /** Session-scoped provider instance cache by sessionKey */ private sessionStores; /** Locks to prevent concurrent session builds (race condition prevention) */ private sessionBuildLocks; /** Maximum number of sessions to cache (LRU eviction) */ private static readonly MAX_SESSION_CACHE_SIZE; /** Session cache TTL in milliseconds (1 hour) */ private static readonly SESSION_CACHE_TTL_MS; /** Cleanup interval in milliseconds (1 minute) */ private static readonly SESSION_CLEANUP_INTERVAL_MS; /** Handle for the session cleanup interval */ private sessionCleanupInterval; /** Whether session caching is enabled (disabled in distributed/serverless mode) */ private readonly sessionCacheEnabled; constructor(list: ProviderType[], parentProviders?: ProviderRegistry | undefined, options?: ProviderRegistryOptions); getProviders(): ProviderEntry[]; /** Walk up the registry chain to find a def for a token. */ private lookupDefInHierarchy; /** Resolve a DEFAULT-scoped dependency from the hierarchy, enforcing scope & instantiation. */ private resolveDefaultFromHierarchy; protected buildMap(list: ProviderType[]): RegistryBuildMapResult; protected buildGraph(): void; protected topoSort(): void; /** Incremental instantiation for DEFAULT providers. * - Skips already-built singletons unless force:true. * - Can limit to a subset via onlyTokens. */ protected initialize(opts?: { force?: boolean; onlyTokens?: Iterable; }): Promise; /** Return the live singleton map as a read-only view. No copying. */ getAllSingletons(): ReadonlyMap; discoveryDeps(rec: ProviderRecord): Token[]; invocationTokens(_token: Token, rec: ProviderRecord): Token[]; /** * Normalize deprecated scopes to their modern equivalents. * * - SESSION → CONTEXT (deprecated) * - REQUEST → CONTEXT (deprecated) * * This enables backwards compatibility while unifying the scope model. */ private normalizeScope; getProviderScope(rec: ProviderRecord): ProviderScope; getScope(): ScopeEntry; private withTimeout; private resolveFactoryArg; /** Build a single DEFAULT-scoped singleton (used by incremental instantiating). */ private initiateOne; private buildIntoStore; private resolveManagedForClass; get(token: Token): T; addRegistry(type: T, value: RegistryType[T]): void; getRegistries(type: T): RegistryType[T][]; getHooksRegistry(): import("../hooks/hook.registry").default; getScopeRegistry(): import("../common").ScopeRegistryInterface; /** bootstrap helper: resolve a dependency usable during app bootstrap (must be GLOBAL). */ resolveBootstrapDep(t: Token): Promise; /** Lightweight, synchronous resolver for app-scoped DI. * - If `cls` is a registered DEFAULT provider token, returns the singleton (must be instantiated). * - If `cls` is SCOPED in DI, throws (use getScoped/buildViews instead). * - Otherwise, if `cls` is a constructable class not registered in DI, returns `new cls()`. * If it defines a synchronous init(), it will be invoked (async init() is NOT awaited). */ resolve(cls: any): T; mergeFromRegistry(providedBy: ProviderRegistry, exported: { token: Token; def: ProviderRecord; /** Instance may be undefined for CONTEXT-scoped providers (built per-request) */ instance: ProviderEntry | undefined; }[]): void; /** * Used by plugins to get the exported provider definitions. */ getProviderInfo(token: Token): { token: import("@frontmcp/di").Reference; def: ProviderRecord; instance: ProviderEntry | undefined; }; injectProvider(injected: Omit): void; addDynamicProviders(dynamicProviders: ProviderType[]): Promise; private getWithParents; getActiveScope(): ScopeEntry; getActiveServer(): FrontMcpServer; /** * Clean up a specific session's provider cache. * Call this when a session is terminated or expired. * * @param sessionKey - The session identifier to clean up */ cleanupSession(sessionKey: string): void; /** * Clean up expired sessions from the cache. * Sessions older than SESSION_CACHE_TTL_MS are removed. * * @returns Number of sessions cleaned up */ cleanupExpiredSessions(): number; /** * Start the background session cleanup timer. * This periodically removes expired sessions from the cache. */ startSessionCleanup(): void; /** * Stop the background session cleanup timer. * Call this when shutting down the server gracefully. */ stopSessionCleanup(): void; /** * Dispose of the registry, cleaning up all resources. * Call this when the registry/scope is being destroyed to prevent: * - Memory leaks from retained interval handles * - Orphaned session cleanup timers * * @example * ```typescript * // In scope teardown * scope.providers.dispose(); * ``` */ dispose(): void; /** * Get session cache statistics for monitoring. */ getSessionCacheStats(): { enabled: boolean; size: number; maxSize: number; ttlMs: number; }; /** * Check if session caching is enabled. * Returns false in distributed/serverless mode. */ isSessionCacheEnabled(): boolean; /** * Build provider instance views for different scopes. * * This method creates a complete view of providers across all scopes: * - GLOBAL: Returns existing singleton instances (read-only) * - CONTEXT: Builds per-context providers (unified session + request) * * Note: For backwards compatibility, SESSION and REQUEST scopes are normalized * to CONTEXT. The returned views include `session` and `request` aliases that * both point to the `context` store. * * @param sessionKey - Unique context/session identifier for CONTEXT-scoped providers * @param contextProviders - Optional pre-built CONTEXT-scoped providers (e.g., FrontMcpContext) * @returns ProviderViews with global and context provider maps (session/request as aliases) */ buildViews(sessionKey: string, contextProviders?: Map): Promise; /** * Build a provider into a store, with access to context and global views for dependencies. */ private buildIntoStoreWithViews; /** * Resolve a dependency from the available views (context → global). */ private resolveFromViews; /** * Get a provider from the given views, checking context → global. * * @param token - The provider token to look up * @param views - The provider views to search * @returns The provider instance * @throws Error if provider not found in any view */ getScoped(token: Token, views: ProviderViews): T; } //# sourceMappingURL=provider.registry.d.ts.map