Module filtering/inclusion-rules

Inclusion rules for filtering manga by match score.

import type { AniListManga } from "../../anilist/types";
import type { KenmeiManga } from "@/api/kenmei/types";
import { isExactMatch } from "./exact-match-checker";
import {
shouldAcceptByCustomRules,
ACCEPT_RULE_CONFIDENCE_FLOOR_EXACT,
ACCEPT_RULE_CONFIDENCE_FLOOR_REGULAR,
} from "./custom-rules";

/**
* Decision result for manga inclusion with optional score adjustment.
* @property shouldInclude - Whether the manga should be included in results.
* @property adjustedScore - The match score, potentially adjusted based on match quality.
* @source
*/
export interface InclusionResult {
shouldInclude: boolean;
adjustedScore: number;
}

/**
* Determines if a manga should be included in exact match results.
* Applies stricter thresholds (0.6+) and custom accept rules for priority inclusion.
* @param manga - The manga to evaluate
* @param score - The match score (0-1)
* @param searchTitle - The original search title
* @param results - Current results array for context
* @param kenmeiManga - Optional Kenmei manga for custom rule evaluation
* @returns Inclusion decision with potentially adjusted score
* @source
*/
export function shouldIncludeMangaExact(
manga: AniListManga,
score: number,
searchTitle: string,
results: AniListManga[],
kenmeiManga?: KenmeiManga,
): InclusionResult {
console.debug(
`[MangaSearchService] 🔍 Checking titles for exact match against "${searchTitle}"`,
);

// Check custom accept rules if kenmeiManga provided
if (kenmeiManga) {
const { shouldAccept, matchedRule } = shouldAcceptByCustomRules(
manga,
kenmeiManga,
);
if (shouldAccept) {
console.debug(
`[MangaSearchService] ✅ Auto-accepting manga "${manga.title?.romaji || manga.title?.english}" due to custom rule: ${matchedRule?.description}`,
);
return {
shouldInclude: true,
adjustedScore: Math.max(score, ACCEPT_RULE_CONFIDENCE_FLOOR_EXACT), // Boost to high confidence
};
}
}

// In exact matching mode, do a thorough check of all titles
// This ensures we don't miss matches due to normalization differences
const foundGoodMatch = isExactMatch(manga, searchTitle);

if (score > 0.6 || foundGoodMatch || results.length <= 2) {
console.debug(
`[MangaSearchService] ✅ Including manga "${manga.title?.romaji || manga.title?.english}" with score: ${score}`,
);
return {
shouldInclude: true,
adjustedScore: foundGoodMatch
? Math.max(score, ACCEPT_RULE_CONFIDENCE_FLOOR_REGULAR)
: score,
};
} else {
console.debug(
`[MangaSearchService] ❌ Excluding manga "${manga.title?.romaji || manga.title?.english}" with score: ${score} (below threshold)`,
);
return { shouldInclude: false, adjustedScore: score };
}
}

/**
* Determines if a manga should be included in regular (non-exact) match results.
* Applies lenient threshold (0.15+) and custom accept rules for priority inclusion.
* @param manga - The manga to evaluate
* @param score - The match score (0-1)
* @param results - Current results array for context
* @param kenmeiManga - Optional Kenmei manga for custom rule evaluation
* @returns Inclusion decision with potentially adjusted score
* @source
*/
export function shouldIncludeMangaRegular(
manga: AniListManga,
score: number,
results: AniListManga[],
kenmeiManga?: KenmeiManga,
): InclusionResult {
// Check custom accept rules if kenmeiManga provided
if (kenmeiManga) {
const { shouldAccept, matchedRule } = shouldAcceptByCustomRules(
manga,
kenmeiManga,
);
if (shouldAccept) {
console.debug(
`[MangaSearchService] ✅ Auto-accepting manga "${manga.title?.romaji || manga.title?.english}" due to custom rule: ${matchedRule?.description}`,
);
return {
shouldInclude: true,
adjustedScore: Math.max(score, ACCEPT_RULE_CONFIDENCE_FLOOR_REGULAR), // Boost to high confidence
};
}
}

if (score > 0.15 || results.length <= 2) {
console.debug(
`[MangaSearchService] ✅ Including manga "${manga.title?.romaji || manga.title?.english}" with score: ${score}`,
);
return { shouldInclude: true, adjustedScore: score };
} else {
console.debug(
`[MangaSearchService] ❌ Excluding manga "${manga.title?.romaji || manga.title?.english}" with score: ${score} (below threshold)`,
);
return { shouldInclude: false, adjustedScore: score };
}
}

Interfaces

InclusionResult

Functions

shouldIncludeMangaExact
shouldIncludeMangaRegular