fix(character-dictionary): prioritize directory-scoped override over leg

- Check directory-scoped overrides before falling back to legacy season-key matches, so a same-directory scoped entry wins over a stale legacy fallback
- Add regression test covering same-directory override vs legacy fallback precedence
- Bump electron-builder/electron-updater/eslint and related transitive deps (bun.lock, package.json)
This commit is contained in:
2026-07-24 18:19:45 -07:00
parent 765df15048
commit 998e4b52af
4 changed files with 119 additions and 197 deletions
@@ -238,6 +238,42 @@ test('manual selection store prefers exact scoped override over legacy fallback'
});
});
test('manual selection store prefers same-directory override over legacy fallback', async () => {
const userDataPath = makeTempDir();
const overrideDir = path.join(userDataPath, 'character-dictionaries');
fs.mkdirSync(overrideDir, { recursive: true });
const directoryScope = 'volumes-jellyfin-anime-toaru-kagaku-no-railgun-season-2';
fs.writeFileSync(
path.join(overrideDir, 'anilist-overrides.json'),
JSON.stringify({
overrides: [
{
seriesKey: 'sisters',
mediaId: 1057,
mediaTitle: 'Legacy fallback',
staleMediaIds: [],
},
{
seriesKey: `${directoryScope}--critical`,
mediaId: 16049,
mediaTitle: 'A Certain Scientific Railgun S',
staleMediaIds: [1057],
},
],
}),
'utf8',
);
const store = createCharacterDictionaryManualSelectionStore({ userDataPath });
assert.deepEqual(await store.getOverride(`${directoryScope}--sisters`), {
seriesKey: `${directoryScope}--critical`,
mediaId: 16049,
mediaTitle: 'A Certain Scientific Railgun S',
staleMediaIds: [1057],
});
});
test('manual selection store keeps overrides separate for different season directories', async () => {
const userDataPath = makeTempDir();
const store = createCharacterDictionaryManualSelectionStore({ userDataPath });
@@ -140,10 +140,8 @@ export function createCharacterDictionaryManualSelectionStore(deps: { userDataPa
getOverride: async (seriesKey: string): Promise<CharacterDictionaryManualSelection | null> => {
const candidates = getLegacySeriesKeyCandidates(seriesKey);
const overrides = readOverrides(filePath);
for (const candidate of candidates) {
const match = overrides.find((entry) => entry.seriesKey === candidate);
if (match) return match;
}
const exactMatch = overrides.find((entry) => entry.seriesKey === candidates[0]);
if (exactMatch) return exactMatch;
const directoryScope = getDirectoryScope(seriesKey);
if (directoryScope) {
const scopedMatches = overrides.filter(
@@ -158,6 +156,10 @@ export function createCharacterDictionaryManualSelectionStore(deps: { userDataPa
};
}
}
for (const candidate of candidates.slice(1)) {
const match = overrides.find((entry) => entry.seriesKey === candidate);
if (match) return match;
}
return null;
},
setOverride: async (selection: CharacterDictionaryManualSelection): Promise<void> => {