find<S> method

S find<S>({
  1. String? tag,
})

Finds and returns a registered instance of type S.

This method performs a hierarchical lookup:

  1. Local Check: Checks the current scope.
  2. Parent Check: Recursively checks parent scopes.

If the dependency is found but not yet instantiated (lazy), it will be created.

Throws an Exception if S is not registered in this scope or any ancestor.

Implementation

S find<S>({String? tag}) {
  _ensureUsable();
  // Tag-less singleton lookups use a direct instance cache.
  if (tag == null) {
    final cached = _instanceCache[S];
    if (cached != null) {
      return cached as S;
    }

    // Type registry is the primary lookup path for untagged dependencies.
    final info = _typeRegistry[S];
    if (info != null) {
      // Reuse existing singleton and seed instance cache.
      final instance = info.instance;
      if (instance != null && !info.isFactory) {
        _instanceCache[S] = instance;
        return instance as S;
      }
      // Lazy registrations instantiate on first resolve.
      final result = _findLocal<S>(
        info as LevitDependency<S>,
        LevitScopeKey.of<S>().debugString,
        null,
      );
      // Factories must never be cached.
      if (!info.isFactory && info.instance != null) {
        _instanceCache[S] = info.instance;
      }
      return result;
    }

    final aliasKey = LevitScopeKey.of<S>();
    final canonicalKey = _aliases[aliasKey];
    if (canonicalKey != null) {
      final result = _findAliasLocal<S>(
        aliasKey: aliasKey,
        canonicalKey: canonicalKey,
      );
      _instanceCache[S] = result;
      return result;
    }

    // Parent resolution cache avoids repeated ancestor traversal.
    final cachedScope = _typeResolutionCache[S];
    if (cachedScope != null) {
      try {
        return cachedScope.find<S>();
      } catch (_) {
        _typeResolutionCache.remove(S);
      }
    }

    // Parent fallback preserves scope hierarchy resolution.
    if (_parentScope != null) {
      try {
        final instance = _parentScope!.find<S>();
        _typeResolutionCache[S] = _parentScope!;
        return instance;
      } catch (_) {
        // Cached parent may be stale after resets; fall through.
      }
    }

    throw Exception(
      'LevitScope($name): Type "$S" is not registered.\n'
      'Not found in scope or any parent.',
    );
  }

  // Tagged lookups use composite key registries.
  final key = _getKey<S>(tag);
  final keyString = key.debugString;

  final info = _registry[key];
  if (info != null) {
    return _findLocal<S>(info as LevitDependency<S>, keyString, tag);
  }

  final canonicalKey = _aliases[key];
  if (canonicalKey != null) {
    return _findAliasLocal<S>(
      aliasKey: key,
      canonicalKey: canonicalKey,
    );
  }

  // Reuse cached ancestor that previously resolved this key.
  final cachedScope = _readCachedScope(key);
  if (cachedScope != null) {
    try {
      return cachedScope.find<S>(tag: tag);
    } catch (_) {
      _resolutionCache.remove(key);
    }
  }

  // Fallback to parent scope chain.
  if (_parentScope != null) {
    try {
      final instance = _parentScope!.find<S>(tag: tag);
      _cacheScope(key, _parentScope!);
      return instance;
    } catch (_) {
      // Cached ancestry may be invalidated by parent resets.
    }
  }

  throw Exception(
    'LevitScope($name): Type "$S" with tag "$tag" is not registered.\n'
    'Not found in scope or any parent.',
  );
}