[econ] add missing items courtesy studio87

This commit is contained in:
Devin Zuczek
2026-10-16 21:42:03 -04:00
parent 4c1b9ca9da
commit 2ba2ad3a4b
4 changed files with 4247 additions and 18 deletions
+35 -12
View File
@@ -276,8 +276,14 @@ const reloadPlus = new Command('reload-plus')
console.log(chalk.green(`✓ ${amount} tokens added to ${credited.length} account(s)`))
})
/** The export `cai-load` reads when no `--file` is given. */
const CUSTOM_AVATAR_ITEM_EXPORT = 'apps/econ/static/db/2025-1-cai.json'
/**
* The exports `cai-load` reads when no `--file` is given, in PRECEDENCE order: each only adds
* ids the ones before it lack, so the studio87 extras never overwrite a 2025 item.
*/
const CUSTOM_AVATAR_ITEM_EXPORTS = [
'apps/econ/static/db/2025-1-cai.json',
'apps/econ/static/db/studio87-extras.json',
]
/**
* Load the first-party custom avatar items into `custom_avatar_item` (owned by the `api`
@@ -293,9 +299,9 @@ const CUSTOM_AVATAR_ITEM_EXPORT = 'apps/econ/static/db/2025-1-cai.json'
const caiLoad = new Command('cai-load')
.description('Load first-party custom avatar items from an export JSON (merges; never deletes)')
.option(
'--file <path>',
'The export to load, relative to the repo root',
CUSTOM_AVATAR_ITEM_EXPORT
'--file <paths...>',
'The export(s) to load, relative to the repo root (default: every committed export)',
CUSTOM_AVATAR_ITEM_EXPORTS
)
.option(
'--dry-run',
@@ -306,18 +312,35 @@ const caiLoad = new Command('cai-load')
.option('--remote', 'Target the deployed database instead of the local dev database.', false)
.action(async (opts) => {
const remote = resolveRemote(opts)
const full = path.isAbsolute(opts.file) ? opts.file : path.join(getRepoRoot(), opts.file)
if (!(await fs.pathExists(full))) throw new Error(`no export at ${opts.file}`)
// Tolerate the BOM a .NET export carries (`JSON.parse` rejects U+FEFF).
const text = (await fs.readFile(full, 'utf8')).replace(/^\uFEFF/, '')
const records = readCustomAvatarItemExport(JSON.parse(text) as unknown)
// A later export only ADDS: an id an earlier one already holds is skipped, never replaced.
// (A duplicate within one export is still refused.)
const records: ReturnType<typeof readCustomAvatarItemExport> = []
const seen = new Set<string>()
for (const file of opts.file) {
const full = path.isAbsolute(file) ? file : path.join(getRepoRoot(), file)
if (!(await fs.pathExists(full))) throw new Error(`no export at ${file}`)
// Tolerate the BOM a .NET export carries (`JSON.parse` rejects U+FEFF).
const text = (await fs.readFile(full, 'utf8')).replace(/^\uFEFF/, '')
let skipped = 0
for (const record of readCustomAvatarItemExport(JSON.parse(text) as unknown)) {
if (seen.has(record.CustomAvatarItemId)) {
skipped++
continue
}
seen.add(record.CustomAvatarItemId)
records.push(record)
}
if (skipped > 0) {
console.log(chalk.yellow(`${file}: skipped ${skipped} id(s) an earlier export holds`))
}
}
if (records.length === 0)
throw new Error('the export holds no records — refusing to call that a load')
const statements = customAvatarItemUpsertStatements(records)
const sql = statements.join('\n')
console.log(
`Merging ${records.length} custom avatar items from ${opts.file} into ${target(remote)}`
`Merging ${records.length} custom avatar items from ${opts.file.join(', ')} into ${target(remote)}`
)
if (opts.dryRun) {
console.log(
@@ -461,7 +484,7 @@ Examples:
$ runx admin grant-plus --username alice # Rec Room Plus; takes effect next login
$ runx admin reload-plus 1000 --remote # +1000 tokens to every Plus subscriber
$ runx admin reload-plus 1000 --dry-run # just list them
$ runx admin cai-load # merge the first-party custom items (local)
$ runx admin cai-load # merge every committed first-party export (local)
$ runx admin cai-load --remote --file path/to/export.json
$ runx admin lookup --username alice --remote`
)