diff --git a/src/lib/services/catalog-repair-live.test.ts b/src/lib/services/catalog-repair-live.test.ts index defa4949..58e39453 100644 --- a/src/lib/services/catalog-repair-live.test.ts +++ b/src/lib/services/catalog-repair-live.test.ts @@ -230,6 +230,36 @@ describeGuard("catalog studio live repair rehearsal (sandbox)", () => { const offer = await reconcileImportedOfferIds(); console.log("REHEARSAL offer reconcile:", JSON.stringify(offer)); + // Every furnidata entry whose classname maps to a unique items_base row + // must have id === offerid === db id (incl. cross-section copies). + const [uniqueRows] = (await db.execute(sql` + SELECT id, item_name FROM items_base + `)) as unknown as [Array<{ id: number; item_name: string }>, unknown]; + const dbById = new Map(); + for (const r of uniqueRows) dbById.set(Number(r.id), r.item_name); + const byName = new Map(); + for (const r of uniqueRows) + byName.set(r.item_name, (byName.get(r.item_name) ?? 0) + 1); + const finalFd = JSON.parse( + await fs.readFile( + path.join(gamedataDir, "config", "FurnitureData.json"), + "utf8", + ), + ) as Record> }>; + let badCrossSection = 0; + for (const section of ["roomitemtypes", "wallitemtypes"] as const) { + for (const e of finalFd[section]?.furnitype ?? []) { + const cn = String(e.classname ?? ""); + if (byName.get(cn) !== 1) continue; + const offer = Number(e.offerid); + if (!Number.isFinite(offer) || offer <= 0) continue; + if (dbById.get(offer) !== cn) continue; + if (Number(e.id) !== offer) badCrossSection++; + } + } + console.log("REHEARSAL cross-section id mismatches:", badCrossSection); + expect(badCrossSection).toBe(0); + const afterState = await audit(); console.log("REHEARSAL after:", JSON.stringify(afterState)); diff --git a/src/lib/services/furni-data.ts b/src/lib/services/furni-data.ts index d8e45ea3..e09f1805 100644 --- a/src/lib/services/furni-data.ts +++ b/src/lib/services/furni-data.ts @@ -536,6 +536,24 @@ export async function reconcileFurniDataWithItemsBase(): Promise(); + for (const row of rows) dbById.set(row.id, row.itemName); + for (const section of ["roomitemtypes", "wallitemtypes"] as const) { + for (const e of furniData[section]?.furnitype ?? []) { + const offer = entryOfferId(e); + if (offer <= 0 || Number(e.id) === offer) continue; + if (dbById.get(offer) !== e.classname) continue; + e.id = offer; + fixedIds++; + } + } + if (fixedIds > 0 || fixedOfferIds > 0) { await writeFurniData(furniData as Record); }