Direct catalog hydration
Synchronize only the Epic catalog graphs a local-first client has discovered.
POST /catalog/hydrate resolves public Epic catalog metadata directly from the authoritative MongoDB collections. It does not require a bulk export or a prebuilt generation.
Send at most 25 unique composite item, platform-qualified asset, or platform-qualified release-app identifiers:
{
"schemaVersion": 2,
"identifiers": [
{
"type": "asset",
"namespace": "example-namespace",
"artifactId": "ExampleGame",
"platform": "Windows"
}
],
"knownRoots": [],
"knownRecords": []
}The response is application/x-ndjson. Read and validate one line at a time. Each line describes exactly one requested root and has one of four statuses:
resolvedsupplies the complete authoritativerecordKeysmembership and only records whose SHA-256 differs fromknownRecords.unchangedconfirms that the supplied rootgraphHashis still current.not-foundauthoritatively clears that root without affecting sibling requests. Missingue, Fab, Unreal, and unknown identifiers use this status.erroris a sanitized root-level failure. Keep existing local data and retry the root later.
Each decoded line is bounded to 2 MiB and 500 records. A malformed or oversized root becomes an isolated error line; it does not discard successfully streamed siblings.
Graph contents
A resolved owning item includes every directly related, subitem, and linked offer. It also includes sibling items from those offers plus their assets and normalized release apps. Expansion stops there and does not recursively traverse unrelated offers.
The discriminated type field selects offer, item, asset, release-app, or offer-item. Offer-item edges preserve every observed relationship source. The primary offer ranking is deterministic: non-prepurchase first, then direct/subitem/linked, then BASE_GAME, DLC, ADD_ON, EDITION, BUNDLE, and CONSUMABLE, newest modification, and lexical composite ID.
graphHash is SHA-256 of stable JSON containing the sorted (recordKey, recordSha256) pairs. Metadata changes therefore invalidate the root even when membership is unchanged.
Local-first application
Apply each line in its own SQLite transaction. Replace root membership only for resolved and not-found, garbage-collect records only after their final root reference disappears, and update only affected search documents. If a stream is interrupted, keep committed roots and requeue the unprocessed identifiers.
The endpoint excludes prices, price history, global build history, signed URLs, and image binaries.
Legacy storage cleanup
After direct hydration is verified in production, an operator may explicitly delete the old materialized collections:
pnpm catalog:cleanup-legacy -- --confirm-delete-legacy-catalog-storageWithout that exact confirmation flag the command refuses to delete data.