more stubs

This commit is contained in:
Devin Zuczek
2026-08-15 13:54:19 -04:00
parent 8ea0caa1e5
commit 11b037a2f1
67 changed files with 38566 additions and 2652 deletions
+39
View File
@@ -85,6 +85,7 @@ import {
JsonObject,
OpaqueJsonBody,
OPTIONAL_AUTHED,
RRPlusSignUpBonus,
SaveOutfitRequest,
SaveOutfitV4Response,
SubscriptionResponse,
@@ -1790,6 +1791,16 @@ const app = new Hono<App>({ strict: false })
(c) => c.json([])
)
// The UGC items a room sells (the creator-made things on sale inside it). Same empty
// stub as the room-economy routes above and asked for on the same room load: nothing
// stores room UGC purchasables yet, and an empty list reads as "this room sells
// nothing" where a 404 stalls the load.
.get(
'/api/ugcPurchasables/v1/items/room/:roomId',
listRoute('A rooms UGC purchasables', 'Empty stub so the client doesnt 404'),
(c) => c.json([])
)
// Unlocked consumables. [Authorize]. The consumables the player has bought (from
// `buyItem`, stored in the `consumable` table), grouped by item into the client's
// unlocked-consumable DTO. A player who has bought none gets an empty list.
@@ -2458,6 +2469,34 @@ const app = new Hono<App>({ strict: false })
c.json([])
)
// The Rec Room Plus sign-up bonus: which bonus is running and the token price window
// the free items are drawn from. Fixed numbers, the same for every caller.
//
// Unauthenticated, like the subscription lookup below and for the same reason: the
// client reads this while putting the RR+ page together, nothing in the answer is
// per-account, and a 401 would only be a way for that load to stall. (The `api` copy of
// this path does validate a token, mirroring the reference server.)
.get(
'/api/CampusCard/v1/SignUpBonus',
describeRoute({
tags: ['Econ'],
summary: 'Rec Room Plus sign-up bonus',
description: [
'The bonus a player gets for taking out Rec Room Plus: which bonus is running',
'(`RRPlusSignUpBonusId`) and the token price window the free items are picked from.',
'Fixed values — nothing here is per-account or stored, so no auth is required and',
'every caller gets the same three numbers.',
].join(' '),
responses: { 200: json(RRPlusSignUpBonus, 'The running sign-up bonus') },
}),
(c) =>
c.json({
RRPlusSignUpBonusId: 3,
MinFreeItemsPrice: 6000,
MaxFreeItemsPrice: 10000,
})
)
// Subscription lookup (Rec Room Plus, the client's `CampusCard`). There is no store to
// buy one from, so the `developer` role stands in for a paid subscription: a developer
// reports an active Gold year, everyone else reports none. Nothing is stored — see
+11
View File
@@ -195,6 +195,17 @@ export const SubscriptionResponse = z.union([
z.object({}).describe('`{}` — no subscription'),
])
/**
* `GET /api/CampusCard/v1/SignUpBonus` — the Rec Room Plus sign-up bonus. Fixed values,
* not per-account: `RRPlusSignUpBonusId` names the bonus that is running and the two
* prices are the token window the free items are drawn from.
*/
export const RRPlusSignUpBonus = z.object({
RRPlusSignUpBonusId: z.int().describe('Which sign-up bonus is running'),
MinFreeItemsPrice: z.int().describe('Lowest token price a free item may have'),
MaxFreeItemsPrice: z.int().describe('Highest token price a free item may have'),
})
/** `POST /api/challenge/v2/updateProgress` — the identifying fields echoed back. */
export const ChallengeProgressResponse = z.object({
ChallengeMapId: z.int(),
@@ -646,6 +646,7 @@ describe('econ endpoints', () => {
'/econ/roomOffer/room/92',
'/econ/roomOffer/room/92/purchaseCounts',
'/econ/roomGiftDropShops/room/92',
'/api/ugcPurchasables/v1/items/room/92',
]) {
const res = await exports.default.fetch(`${ORIGIN}${path}`)
expect(res.status, path).toBe(200)
@@ -2031,6 +2032,18 @@ describe('econ endpoints', () => {
headers,
})
// Fixed values, and no auth: the client reads this while assembling the RR+ page, so a
// 401 would only be a way for that load to stall.
test('GET /api/CampusCard/v1/SignUpBonus returns the running bonus, unauthenticated', async () => {
const res = await exports.default.fetch(`${ORIGIN}/api/CampusCard/v1/SignUpBonus`)
expect(res.status).toBe(200)
expect(await res.json()).toEqual({
RRPlusSignUpBonusId: 3,
MinFreeItemsPrice: 6000,
MaxFreeItemsPrice: 10000,
})
})
test('POST /api/CampusCard/v1/UpdateAndGetSubscription gives a developer a Gold year', async () => {
const res = await getSubscription(await bearer('205', ['gameClient', 'developer']))
expect(res.status).toBe(200)
@@ -2101,6 +2114,7 @@ describe('econ endpoints', () => {
)
)
expect([...documented].sort()).toEqual([
'GET /api/CampusCard/v1/SignUpBonus',
'GET /api/avatar/v1/defaultbaseavataritems',
'GET /api/avatar/v1/defaultunlocked',
'GET /api/avatar/v2',
@@ -2127,6 +2141,7 @@ describe('econ endpoints', () => {
'GET /api/storefronts/v2/buyInvention',
'GET /api/storefronts/v3/giftdropstore/{id}',
'GET /api/storefronts/v4/balance/{currencyType}',
'GET /api/ugcPurchasables/v1/items/room/{roomId}',
'GET /econ/customAvatarItems/v1/owned',
'GET /econ/roomGiftDropShops/room/{roomId}',
'GET /econ/roomInventory/room/{roomId}',