• Execute paginated search loop collecting results until completion.

    Continues pagination while results available, max not reached, and not aborted. Respects rate limiting between requests.

    Parameters

    • searchQuery: string

      Search query string

    • searchConfig: SearchServiceConfig

      Search configuration

    • token: undefined | string

      Optional authentication token

    • abortSignal: undefined | AbortSignal

      Optional abort signal to cancel search

    • OptionalspecificPage: number

      Optional specific page number (disables pagination)

    Returns Promise<SearchLoopResult>

    Promise with collected results and final page info

    export async function executeSearchLoop(
    searchQuery: string,
    searchConfig: SearchServiceConfig,
    token: string | undefined,
    abortSignal: AbortSignal | undefined,
    specificPage?: number,
    ): Promise<SearchLoopResult> {
    let results: AniListManga[] = [];
    let currentPage = specificPage || 1;
    let hasNextPage = true;
    let lastPageInfo: PageInfo | undefined = undefined;
    const isSinglePageMode = specificPage !== undefined;

    console.info(
    `[MangaSearchService] 🌐 Making network request to AniList API for "${searchQuery}" - bypassCache=${searchConfig.bypassCache}`,
    );

    while (hasNextPage && results.length < searchConfig.maxSearchResults) {
    try {
    if (abortSignal?.aborted) {
    throw new Error("Search aborted by abort signal");
    }

    // Execute the search request
    const searchResult = await executeSingleSearch(
    searchQuery,
    currentPage,
    searchConfig,
    token,
    );

    console.debug(
    `[MangaSearchService] 🔍 Search response for "${searchQuery}" page ${currentPage}: ${searchResult?.Page?.media?.length || 0} results`,
    );

    // Log detailed results if cache is bypassed
    if (searchConfig.bypassCache && searchResult?.Page?.media?.length > 0) {
    console.debug(
    `[MangaSearchService] 🔍 Titles received from API:`,
    searchResult.Page.media.map((m) => ({
    id: m.id,
    romaji: m.title?.romaji,
    english: m.title?.english,
    native: m.title?.native,
    synonyms: m.synonyms?.length,
    })),
    );
    }

    // Validate search result structure
    if (!validateSearchResult(searchResult, searchQuery)) {
    break;
    }

    // Add results to collection
    results = [...results, ...searchResult.Page.media];
    lastPageInfo = searchResult.Page.pageInfo;

    // Check if pagination should continue
    hasNextPage = shouldContinuePagination(
    searchResult.Page.pageInfo,
    currentPage,
    results.length,
    searchConfig.maxSearchResults,
    isSinglePageMode,
    );

    currentPage++;

    if (hasNextPage) {
    await acquireRateLimit();
    }
    } catch (error: unknown) {
    handleSearchError(error, searchQuery);
    throw error;
    }
    }

    return { results, lastPageInfo };
    }