{"version":3,"file":"service-invalidator.mjs","names":["InstanceStatus","ServiceInvalidator","eventBus","logger","invalidateWithStorage","service","storage","options","emitEvents","onInvalidated","log","result","get","holder","invalidateHolderWithStorage","setupDependencySubscriptions","serviceName","dependencies","dependencyName","unsubscribe","on","catch","error","destroyListeners","push","clearAllWithStorage","waitForSettlement","readyWithStorage","allServiceNames","getAllNames","length","join","clearPromises","map","Promise","all","holders","forEach","_","waitForHolderToSettle","key","invalidateHolderByStatus","context","onDestroy","destroyHolderWithStorage","status","Destroying","destroyPromise","Creating","creationPromise","listener","then","deps","clear","delete","emitInstanceEvent","Created","Error","name","event","resolve","emit"],"sources":["../../../../src/internal/core/service-invalidator.mts"],"sourcesContent":["import type { IHolderStorage } from '../holder/holder-storage.interface.mjs'\nimport type { InstanceHolder } from '../holder/instance-holder.mjs'\nimport type { LifecycleEventBus } from '../lifecycle/lifecycle-event-bus.mjs'\n\nimport { InstanceStatus } from '../holder/instance-holder.mjs'\n\nexport interface ClearAllOptions {\n  /** Whether to wait for all services to settle before starting (default: true) */\n  waitForSettlement?: boolean\n}\n\nexport interface InvalidationOptions {\n  /** Whether to emit events after invalidation (default: true) */\n  emitEvents?: boolean\n  /** Custom event emitter function */\n  onInvalidated?: (instanceName: string) => Promise<void>\n  /** Whether to cascade invalidation to dependents (default: false - events handle it) */\n  cascade?: boolean\n}\n\n/**\n * Manages graceful service cleanup with event-based invalidation.\n *\n * Uses event subscriptions instead of manual dependent finding.\n * When a service is created, it subscribes to destroy events of its dependencies.\n * When a dependency is destroyed, the event automatically invalidates dependents.\n */\nexport class ServiceInvalidator {\n  constructor(\n    private readonly eventBus: LifecycleEventBus | null,\n    private readonly logger: Console | null = null,\n  ) {}\n\n  /**\n   * Invalidates a service using a specific storage.\n   * Event-based invalidation means dependents are automatically invalidated\n   * via destroy event subscriptions - no need to manually find dependents.\n   *\n   * @param service The instance name to invalidate\n   * @param storage The storage to use for this invalidation\n   * @param options Additional options for invalidation behavior\n   */\n  async invalidateWithStorage(\n    service: string,\n    storage: IHolderStorage,\n    options: InvalidationOptions = {},\n  ): Promise<void> {\n    const { emitEvents = true, onInvalidated } = options\n\n    this.logger?.log(\n      `[ServiceInvalidator] Starting invalidation process for ${service}`,\n    )\n\n    const result = storage.get(service)\n    if (result === null) {\n      return\n    }\n\n    const [, holder] = result\n    if (holder) {\n      await this.invalidateHolderWithStorage(\n        service,\n        holder,\n        storage,\n        emitEvents,\n        onInvalidated,\n      )\n    }\n  }\n\n  /**\n   * Sets up destroy event subscriptions for a service's dependencies.\n   * Called when a service is successfully instantiated.\n   *\n   * @param serviceName The name of the service\n   * @param dependencies The set of dependency names\n   * @param storage The storage to use for invalidation\n   * @param holder The holder for the service (to add unsubscribe to destroy listeners)\n   */\n  setupDependencySubscriptions(\n    serviceName: string,\n    dependencies: Set<string>,\n    storage: IHolderStorage,\n    holder: InstanceHolder,\n  ): void {\n    if (!this.eventBus) {\n      return\n    }\n\n    for (const dependencyName of dependencies) {\n      // Subscribe to the dependency's destroy event\n      const unsubscribe = this.eventBus.on(dependencyName, 'destroy', () => {\n        this.logger?.log(\n          `[ServiceInvalidator] Dependency ${dependencyName} destroyed, invalidating ${serviceName}`,\n        )\n        // Automatically invalidate this service when dependency is destroyed\n        this.invalidateWithStorage(serviceName, storage).catch((error) => {\n          this.logger?.error(\n            `[ServiceInvalidator] Error invalidating ${serviceName} after dependency ${dependencyName} destroyed:`,\n            error,\n          )\n        })\n      })\n\n      // Store unsubscribe function in the service's destroy listeners\n      // so it's cleaned up when the service is destroyed\n      holder.destroyListeners.push(unsubscribe)\n    }\n  }\n\n  /**\n   * Gracefully clears all services in a specific storage.\n   * This allows clearing request-scoped services using a RequestStorage.\n   */\n  async clearAllWithStorage(\n    storage: IHolderStorage,\n    options: ClearAllOptions = {},\n  ): Promise<void> {\n    const { waitForSettlement = true } = options\n\n    this.logger?.log(\n      '[ServiceInvalidator] Starting graceful clearing of all services',\n    )\n\n    // Wait for all services to settle if requested\n    if (waitForSettlement) {\n      this.logger?.log(\n        '[ServiceInvalidator] Waiting for all services to settle...',\n      )\n      await this.readyWithStorage(storage)\n    }\n\n    // Get all service names that need to be cleared\n    const allServiceNames = storage.getAllNames()\n\n    if (allServiceNames.length === 0) {\n      this.logger?.log('[ServiceInvalidator] No services to clear')\n    } else {\n      this.logger?.log(\n        `[ServiceInvalidator] Found ${allServiceNames.length} services to clear: ${allServiceNames.join(', ')}`,\n      )\n\n      // Clear services - events will handle dependent invalidation\n      const clearPromises = allServiceNames.map((serviceName) =>\n        this.invalidateWithStorage(serviceName, storage),\n      )\n\n      await Promise.all(clearPromises)\n    }\n\n    this.logger?.log('[ServiceInvalidator] Graceful clearing completed')\n  }\n\n  /**\n   * Waits for all services in a specific storage to settle.\n   */\n  async readyWithStorage(storage: IHolderStorage): Promise<void> {\n    const holders: InstanceHolder<any>[] = []\n    storage.forEach((_: string, holder: InstanceHolder) => holders.push(holder))\n    await Promise.all(\n      holders.map((holder) => this.waitForHolderToSettle(holder)),\n    )\n  }\n\n  // ============================================================================\n  // INTERNAL INVALIDATION HELPERS\n  // ============================================================================\n\n  /**\n   * Invalidates a single holder using a specific storage.\n   */\n  private async invalidateHolderWithStorage(\n    key: string,\n    holder: InstanceHolder<any>,\n    storage: IHolderStorage,\n    emitEvents: boolean,\n    onInvalidated?: (instanceName: string) => Promise<void>,\n  ): Promise<void> {\n    await this.invalidateHolderByStatus(holder, {\n      context: key,\n      onDestroy: () =>\n        this.destroyHolderWithStorage(\n          key,\n          holder,\n          storage,\n          emitEvents,\n          onInvalidated,\n        ),\n    })\n  }\n\n  /**\n   * Common invalidation logic for holders based on their status.\n   */\n  private async invalidateHolderByStatus(\n    holder: InstanceHolder<any>,\n    options: {\n      context: string\n      onDestroy: () => Promise<void>\n    },\n  ): Promise<void> {\n    switch (holder.status) {\n      case InstanceStatus.Destroying:\n        await holder.destroyPromise\n        break\n\n      case InstanceStatus.Creating:\n        // Wait for creation to complete before destroying\n        await holder.creationPromise\n        await options.onDestroy()\n        break\n\n      default:\n        await options.onDestroy()\n        break\n    }\n  }\n\n  /**\n   * Destroys a holder using a specific storage.\n   */\n  private async destroyHolderWithStorage(\n    key: string,\n    holder: InstanceHolder<any>,\n    storage: IHolderStorage,\n    emitEvents: boolean,\n    onInvalidated?: (instanceName: string) => Promise<void>,\n  ): Promise<void> {\n    holder.status = InstanceStatus.Destroying\n    this.logger?.log(\n      `[ServiceInvalidator] Invalidating ${key} and notifying listeners`,\n    )\n\n    holder.destroyPromise = Promise.all(\n      holder.destroyListeners.map((listener) => listener()),\n    ).then(async () => {\n      holder.destroyListeners = []\n      holder.deps.clear()\n      storage.delete(key)\n\n      // Emit events if enabled and event bus exists\n      if (emitEvents && this.eventBus) {\n        await this.emitInstanceEvent(key, 'destroy')\n      }\n\n      // Call custom callback if provided\n      if (onInvalidated) {\n        await onInvalidated(key)\n      }\n    })\n\n    await holder.destroyPromise\n  }\n\n  /**\n   * Waits for a holder to settle (either created, destroyed, or error state).\n   */\n  private async waitForHolderToSettle(\n    holder: InstanceHolder<any>,\n  ): Promise<void> {\n    switch (holder.status) {\n      case InstanceStatus.Creating:\n        await holder.creationPromise\n        break\n      case InstanceStatus.Destroying:\n        await holder.destroyPromise\n        break\n      // Already settled states\n      case InstanceStatus.Created:\n      case InstanceStatus.Error:\n        break\n    }\n  }\n\n  /**\n   * Emits events to listeners for instance lifecycle events.\n   */\n  private emitInstanceEvent(\n    name: string,\n    event: 'create' | 'destroy' = 'create',\n  ) {\n    if (!this.eventBus) {\n      return Promise.resolve()\n    }\n    this.logger?.log(\n      `[ServiceInvalidator]#emitInstanceEvent() Notifying listeners for ${name} with event ${event}`,\n    )\n    return this.eventBus.emit(name, event)\n  }\n}\n"],"mappings":"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"}