Fix PF2e actions: dedupe junk, show action cost, add cost filter

The AoN action index was polluted with ~2,300 trait-fragment entries
('(concentrate)', 'envision', 'command'...) and the same basic actions repeated
per source. Filter to real Title-cased names and dedupe by name (keep richest):
3,950 -> 646 real actions (also cleaned in the live data file, 3.9M -> 859K).

- pf2eMeta now shows action cost (Single Action/Reaction/...) instead of 'common'
- Added an Action cost filter to the Actions category
- Hide noisy 'common' rarity in PF2e detail facts
- Scraper postProcess() makes future fetches clean too

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-08 01:10:00 +02:00
parent 2c08a26f21
commit 1c87b0e3aa
4 changed files with 45 additions and 6 deletions
+25 -1
View File
@@ -95,12 +95,36 @@ function normalize(src: Record<string, unknown>): Record<string, unknown> {
return out;
}
/**
* The AoN `action` index is polluted with action-trait fragments (names like
* "(concentrate)", "envision", "command") and the same basic actions repeated
* once per source. Keep only real Title-Cased action names and dedupe by name,
* preferring the entry with the most complete text.
*/
function isRealAction(x: Record<string, unknown>): boolean {
const n = String(x.name ?? '').trim();
return n !== '' && /^[A-Z0-9"]/.test(n) && !n.startsWith('(');
}
function dedupeByName(list: Record<string, unknown>[]): Record<string, unknown>[] {
const best = new Map<string, Record<string, unknown>>();
for (const x of list) {
const k = String(x.name);
const cur = best.get(k);
if (!cur || String(x.text ?? '').length > String(cur.text ?? '').length) best.set(k, x);
}
return [...best.values()].sort((a, b) => String(a.name).localeCompare(String(b.name)));
}
function postProcess(category: string, list: Record<string, unknown>[]): Record<string, unknown>[] {
if (category === 'action') return dedupeByName(list.filter(isRealAction));
return list;
}
async function main() {
await mkdir(OUT_DIR, { recursive: true });
const index: { category: string; file: string; count: number }[] = [];
for (const [category, file] of Object.entries(CATEGORIES)) {
const entries = await fetchCategory(category);
const entries = postProcess(category, await fetchCategory(category));
const path = join(OUT_DIR, `${file}.json`);
await writeFile(path, JSON.stringify(entries));
index.push({ category, file, count: entries.length });