fix: reorder catalog missing-entry scan after structural repair
runCatalogAudit computed missingCatalogEntries from the pre-repair snapshot, so generated catalog SQL could reference sprite ids deleted by the duplicate-classname merge and create new orphaned catalog_items rows. Re-derive entries after repairStructure and add a defensive guard in generateCatalogSql that skips entries whose item id no longer exists in items_base. Also fix the catalog_pages INSERT template (18 columns vs 17 values) which made page creation always fail with 'Column count doesn't match value count'. Add a sandbox-guarded live repair rehearsal test covering the full pipeline.
This commit is contained in:
1 parent
60b6d0856c
commit
3a63152ee3
3 files changed
+318
-2
No files matched your search
@@ -662,6 +662,65 @@ export async function runCatalogAudit(
|
||||
remappedReferences,
|
||||
},
|
||||
});
|
||||
|
||||
// Structural repair can DELETE items_base rows (duplicate classname
|
||||
// merge) and rewrite catalog_items.item_ids, so the missing-catalog
|
||||
// list computed from the pre-repair snapshot is stale: entries may
|
||||
// reference ids that no longer exist. Re-derive it from the repaired
|
||||
// state, otherwise the generated catalog SQL would insert orphaned
|
||||
// catalog_items rows (item_ids pointing at deleted sprite ids).
|
||||
if (duplicateRowsRemoved > 0 || orphanedRemoved > 0) {
|
||||
const [postItems] = (await db.execute(sql`
|
||||
SELECT id, item_name, public_name, type, interaction_type
|
||||
FROM items_base ORDER BY id
|
||||
`)) as unknown as [
|
||||
Array<{
|
||||
id: number;
|
||||
item_name: string;
|
||||
public_name: string;
|
||||
type: string;
|
||||
interaction_type: string;
|
||||
}>,
|
||||
unknown,
|
||||
];
|
||||
items = postItems;
|
||||
|
||||
const [postCatalog] = (await db.execute(sql`
|
||||
SELECT id, item_ids, catalog_name FROM catalog_items ORDER BY id
|
||||
`)) as unknown as [
|
||||
Array<{ id: number; item_ids: string; catalog_name: string }>,
|
||||
unknown,
|
||||
];
|
||||
const postCatalogIds = new Set<number>();
|
||||
for (const ci of postCatalog) {
|
||||
const ids = ci.item_ids.split(";").map(Number).filter(Boolean);
|
||||
for (const id of ids) postCatalogIds.add(id);
|
||||
}
|
||||
|
||||
missingCatalogEntries.length = 0;
|
||||
for (const item of items) {
|
||||
let kind = classifyCatalogItem(item);
|
||||
if (
|
||||
kind === "furni" &&
|
||||
(isBadgeType(item.type) || badgeGifSet.has(item.item_name))
|
||||
) {
|
||||
kind = "badge";
|
||||
}
|
||||
if (NO_CATALOG_KINDS.has(kind)) continue;
|
||||
if (postCatalogIds.has(item.id)) continue;
|
||||
const hasNitro = assetNameCandidates(item.item_name).nitro.some((n) =>
|
||||
nitroSet.has(n),
|
||||
);
|
||||
if (kind === "pet" || hasNitro) {
|
||||
missingCatalogEntries.push({
|
||||
classname: item.item_name,
|
||||
itemId: item.id,
|
||||
itemType: item.type,
|
||||
publicName: item.public_name,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
onEvent?.({
|
||||
type: "error",
|
||||
|
||||
Reference in new issue
Block a user