Prioritize newest missing sets in catalog sync queue. (#49)

The cron was importing oldest MTG sets first and never reaching recent Pokémon releases like Perfect Order; merge MTG and Pokémon by release date descending instead.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
varutasu 2026-05-27 21:49:58 -05:00 committed by GitHub
parent 0a47362103
commit bec0a7abbd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 95 additions and 8 deletions

View file

@ -24,7 +24,7 @@ export function filterMissingMtgSets(scryfallSets, knownCodes, now = new Date())
}); });
} }
missing.sort((a, b) => a.releasedAt.localeCompare(b.releasedAt)); missing.sort((a, b) => b.releasedAt.localeCompare(a.releasedAt));
return missing; return missing;
} }
@ -46,7 +46,14 @@ export function filterMissingPokemonSets(pokemonSets, knownCodes) {
}); });
} }
missing.sort((a, b) => (a.releasedAt || '').localeCompare(b.releasedAt || '')); missing.sort((a, b) => {
const dateA = a.releasedAt || '';
const dateB = b.releasedAt || '';
if (!dateA && !dateB) return 0;
if (!dateA) return 1;
if (!dateB) return -1;
return dateB.localeCompare(dateA);
});
return missing; return missing;
} }

View file

@ -7,6 +7,37 @@ const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const DEFAULT_MAX_SETS_PER_RUN = 3; const DEFAULT_MAX_SETS_PER_RUN = 3;
const DEFAULT_DELAY_MS = 1500; const DEFAULT_DELAY_MS = 1500;
/**
* Merge missing MTG + Pokémon sets and pick the newest releases first.
*/
export function buildCatalogSyncQueue(missingMtg, missingPokemon, maxSetsPerRun = DEFAULT_MAX_SETS_PER_RUN) {
const queue = [
...missingMtg.map((set) => ({
game: 'mtg',
setCode: set.code,
name: set.name,
releasedAt: set.releasedAt,
})),
...missingPokemon.map((set) => ({
game: 'pokemon',
setCode: set.id,
name: set.name,
releasedAt: set.releasedAt,
})),
];
queue.sort((a, b) => {
const dateA = a.releasedAt || '';
const dateB = b.releasedAt || '';
if (!dateA && !dateB) return 0;
if (!dateA) return 1;
if (!dateB) return -1;
return dateB.localeCompare(dateA);
});
return queue.slice(0, maxSetsPerRun);
}
/** /**
* Discover missing MTG + Pokémon sets and import up to maxSetsPerRun, paced for upstream APIs. * Discover missing MTG + Pokémon sets and import up to maxSetsPerRun, paced for upstream APIs.
*/ */
@ -19,10 +50,7 @@ export async function runCatalogSync(options = {}) {
discoverMissingPokemonSets(), discoverMissingPokemonSets(),
]); ]);
const queue = [ const queue = buildCatalogSyncQueue(missingMtg, missingPokemon, maxSetsPerRun);
...missingMtg.map((set) => ({ game: 'mtg', setCode: set.code, name: set.name })),
...missingPokemon.map((set) => ({ game: 'pokemon', setCode: set.id, name: set.name })),
].slice(0, maxSetsPerRun);
const summary = { const summary = {
imported: 0, imported: 0,

View file

@ -8,12 +8,13 @@ import {
describe('filterMissingMtgSets', () => { describe('filterMissingMtgSets', () => {
const now = new Date('2026-05-27T12:00:00.000Z'); const now = new Date('2026-05-27T12:00:00.000Z');
it('returns released paper sets missing from the catalog', () => { it('returns released paper sets missing from the catalog, newest first', () => {
const known = new Set(['lea']); const known = new Set(['lea']);
const missing = filterMissingMtgSets( const missing = filterMissingMtgSets(
[ [
{ code: 'lea', name: 'Alpha', released_at: '1993-08-05', set_type: 'core', digital: false }, { code: 'lea', name: 'Alpha', released_at: '1993-08-05', set_type: 'core', digital: false },
{ code: 'fin', name: 'Final Fantasy', released_at: '2026-05-01', set_type: 'expansion', digital: false }, { code: 'fin', name: 'Final Fantasy', released_at: '2026-05-01', set_type: 'expansion', digital: false },
{ code: 'dsk', name: 'Duskmourn', released_at: '2024-09-27', set_type: 'expansion', digital: false },
{ code: 'tlea', name: 'Alpha Tokens', released_at: '1993-08-05', set_type: 'token', digital: false }, { code: 'tlea', name: 'Alpha Tokens', released_at: '1993-08-05', set_type: 'token', digital: false },
{ code: 'mh3', name: 'Modern Horizons 3', released_at: '2027-01-01', set_type: 'expansion', digital: false }, { code: 'mh3', name: 'Modern Horizons 3', released_at: '2027-01-01', set_type: 'expansion', digital: false },
], ],
@ -27,22 +28,33 @@ describe('filterMissingMtgSets', () => {
name: 'Final Fantasy', name: 'Final Fantasy',
releasedAt: '2026-05-01', releasedAt: '2026-05-01',
}, },
{
code: 'dsk',
name: 'Duskmourn',
releasedAt: '2024-09-27',
},
]); ]);
}); });
}); });
describe('filterMissingPokemonSets', () => { describe('filterMissingPokemonSets', () => {
it('returns sets whose ids are not in the catalog', () => { it('returns sets whose ids are not in the catalog, newest first', () => {
const known = new Set(['sv1']); const known = new Set(['sv1']);
const missing = filterMissingPokemonSets( const missing = filterMissingPokemonSets(
[ [
{ id: 'sv1', name: 'Scarlet & Violet', releaseDate: '2023-03-31' }, { id: 'sv1', name: 'Scarlet & Violet', releaseDate: '2023-03-31' },
{ id: 'sv2', name: 'Paldea Evolved', releaseDate: '2023-06-09' }, { id: 'sv2', name: 'Paldea Evolved', releaseDate: '2023-06-09' },
{ id: 'sv3', name: 'Obsidian Flames', releaseDate: '2023-08-11' },
], ],
known known
); );
expect(missing).toEqual([ expect(missing).toEqual([
{
id: 'sv3',
name: 'Obsidian Flames',
releasedAt: '2023-08-11',
},
{ {
id: 'sv2', id: 'sv2',
name: 'Paldea Evolved', name: 'Paldea Evolved',

View file

@ -0,0 +1,40 @@
import { describe, expect, it } from 'vitest';
import { buildCatalogSyncQueue } from '../../lib/card-import/sync-catalog.js';
describe('buildCatalogSyncQueue', () => {
it('merges MTG and Pokémon missing sets by release date, newest first', () => {
const queue = buildCatalogSyncQueue(
[
{ code: 'fin', name: 'Final Fantasy', releasedAt: '2026-05-01' },
{ code: 'dsk', name: 'Duskmourn', releasedAt: '2024-09-27' },
],
[
{ id: 'sv10', name: 'Perfect Order', releasedAt: '2026-03-27' },
{ id: 'sv9', name: 'Journey Together', releasedAt: '2025-03-28' },
],
3
);
expect(queue).toEqual([
{
game: 'mtg',
setCode: 'fin',
name: 'Final Fantasy',
releasedAt: '2026-05-01',
},
{
game: 'pokemon',
setCode: 'sv10',
name: 'Perfect Order',
releasedAt: '2026-03-27',
},
{
game: 'pokemon',
setCode: 'sv9',
name: 'Journey Together',
releasedAt: '2025-03-28',
},
]);
});
});