• Search for manga by title with rate limiting and caching.

    Parameters

    • title: string

      The manga title to search for.

    • Optionaltoken: string

      Optional authentication token.

    • config: Partial<SearchServiceConfig> = {}

      Optional search service configuration.

    • OptionalabortSignal: AbortSignal

      Optional abort signal to cancel the search.

    • OptionalspecificPage: number

    Returns Promise<MangaSearchResponse>

    A promise resolving to an array of MangaMatch objects.

    export async function searchMangaByTitle(
    title: string,
    token?: string,
    config: Partial<SearchServiceConfig> = {},
    abortSignal?: AbortSignal,
    specificPage?: number,
    ): Promise<MangaSearchResponse> {
    const searchConfig = { ...DEFAULT_SEARCH_CONFIG, ...config };
    const cacheKey = generateCacheKey(title);

    // Handle cache operations
    if (searchConfig.bypassCache && cacheKey) {
    handleCacheBypass(title, cacheKey);
    } else if (!searchConfig.bypassCache) {
    const cachedResult = processCachedResults(title, cacheKey);
    if (cachedResult) {
    return cachedResult;
    }
    } else if (searchConfig.exactMatchingOnly) {
    console.log(
    `🔍 MANUAL SEARCH: Ensuring exact matching is correctly configured`,
    );
    searchConfig.exactMatchingOnly = true;
    }

    // Execute the search
    const searchQuery = title;
    await acquireRateLimit();

    const { results, lastPageInfo } = await executeSearchLoop(
    searchQuery,
    searchConfig,
    token,
    abortSignal,
    specificPage,
    );

    // Process and filter results
    const rankedResults = processSearchResults(results, title, searchConfig);
    let filteredResults = applyContentFiltering(
    rankedResults,
    title,
    searchConfig,
    );
    filteredResults = handleNoResultsFallback(
    filteredResults,
    results,
    searchConfig,
    );

    // Handle fallback sources only if no original AniList results were found
    let finalResults = filteredResults;
    let comickSourceMap = new Map<
    number,
    { title: string; slug: string; comickId: string; foundViaComick: boolean }
    >();
    let mangaDexSourceMap = new Map<
    number,
    {
    title: string;
    slug: string;
    mangaDexId: string;
    foundViaMangaDex: boolean;
    }
    >();

    // Only use fallback sources if no AniList results were found
    if (filteredResults.length === 0) {
    console.log(
    `🎯 No AniList results found for "${title}", trying fallback sources...`,
    );

    // Try both fallback sources when enabled
    const comickFallback = await executeComickFallback(
    title,
    token,
    finalResults,
    searchConfig,
    );
    const mangaDexFallback = await executeMangaDexFallback(
    title,
    token,
    finalResults,
    searchConfig,
    );

    // Merge results and handle duplicates
    const mergedResults = mergeSourceResults(
    finalResults,
    comickFallback.results,
    mangaDexFallback.results,
    comickFallback.comickSourceMap,
    mangaDexFallback.mangaDexSourceMap,
    );

    finalResults = mergedResults.mergedResults;
    comickSourceMap = mergedResults.comickSourceMap;
    mangaDexSourceMap = mergedResults.mangaDexSourceMap;
    } else {
    console.log(
    `✅ Found ${filteredResults.length} AniList results for "${title}", skipping fallback sources`,
    );
    }

    // Build and return final response
    return buildFinalResponse(
    finalResults,
    title,
    comickSourceMap,
    mangaDexSourceMap,
    lastPageInfo,
    );
    }