From 8562166d78e6f4ea3df491253bfb318bce6aa24d Mon Sep 17 00:00:00 2001 From: Joao Paulo Furtado Date: Tue, 27 Oct 2026 21:25:34 -0700 Subject: [PATCH] fix(release): publish index rows in the engine reader's five-field contract The engine's reference reader rejects unknown fields: its index validator accepts exactly id, title, platform, repo and tier per game row, so the richer catalog rows (kind, rating, genre, subgenre) made every release index unreadable with TN_REFERENCES_MALFORMED. Project the rows at publish time; rating, genre and kind stay in data/catalog.json, where this library needs them. Verified against the engine source contract at threenative-engine packages/engine-mcp/src/references.ts (IReferenceGameRow). --- scripts/validate.mjs | 5 ++++- tests/library.spec.mjs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/validate.mjs b/scripts/validate.mjs index 0ef144d..512b85a 100644 --- a/scripts/validate.mjs +++ b/scripts/validate.mjs @@ -178,7 +178,10 @@ export function run(args = process.argv.slice(2)) { ]); validateCatalog(catalog, games, stubs); mkdirSync(path.join(root, '.release'), { recursive: true }); - writeFileSync(path.join(root, '.release', 'index.json.gz'), gzipSync(JSON.stringify({ version: 1, games: catalog, stubs }))); + // The engine reference reader rejects unknown fields, so the index carries exactly the five + // row fields it declares; rating, genre and kind stay in data/catalog.json. + const rows = catalog.map(({ id, title, platform, repo: source, tier }) => ({ id, title, platform, repo: source, tier })); + writeFileSync(path.join(root, '.release', 'index.json.gz'), gzipSync(JSON.stringify({ version: 1, games: rows, stubs }))); for (const game of games) writeFileSync(path.join(root, '.release', `game-${game.id}.json`), JSON.stringify(game)); } } diff --git a/tests/library.spec.mjs b/tests/library.spec.mjs index f15de9f..0927120 100644 --- a/tests/library.spec.mjs +++ b/tests/library.spec.mjs @@ -142,7 +142,7 @@ test('release assets keep the version-1 contract and exact asset set', t => { assert.deepEqual(readdirSync(path.join(dir, '.release')).sort(), ['game-demo.json', 'game-second.json', 'index.json.gz']); const index = JSON.parse(gunzipSync(readFileSync(path.join(dir, '.release', 'index.json.gz'))).toString()); assert.equal(index.version, 1); - assert.deepEqual(index.games, config); + assert.deepEqual(index.games, config.map(({ id, title, platform, repo, tier }) => ({ id, title, platform, repo, tier }))); const game = demoGame(source.sha); assert.equal(readFileSync(path.join(dir, '.release', 'game-demo.json'), 'utf8'), JSON.stringify(game)); assert.doesNotThrow(() => validateGame(JSON.parse(readFileSync(path.join(dir, '.release', 'game-demo.json'), 'utf8'))));